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 
14 #include <algorithm>
15 #include <cstdlib>
16 #include <cstring>
17 #include <format>
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>(std::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             std::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>(std::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         std::string fmt;
171         for (auto value : targetInfo.physBinPath)
172         {
173             fmt += std::format("{:02X} ", value);
174         }
175 
176         log<level::ERR>(std::format("Given ATTR_PHYS_BIN_PATH value {} "
177                                     "not found in phal device tree",
178                                     fmt)
179                             .c_str());
180         return false;
181     }
182     else if (ret == requireAttrNotFound)
183     {
184         return false;
185     }
186 
187     return true;
188 }
189 
190 /**
191  * @brief GET PEL priority from pHAL priority
192  *
193  * The pHAL callout priority is in different format than PEL format
194  * so, this api is used to return current phal supported priority into
195  * PEL expected format.
196  *
197  * @param[in] phalPriority used to pass phal priority format string
198  *
199  * @return pel priority format string else empty if failure
200  *
201  * @note For "NONE" returning "L" (LOW)
202  */
203 static std::string getPelPriority(const std::string& phalPriority)
204 {
205     const std::map<std::string, std::string> priorityMap = {
206         {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}};
207 
208     auto it = priorityMap.find(phalPriority);
209     if (it == priorityMap.end())
210     {
211         log<level::ERR>(std::format("Unsupported phal priority({}) is given "
212                                     "to get pel priority format",
213                                     phalPriority)
214                             .c_str());
215         return "H";
216     }
217 
218     return it->second;
219 }
220 
221 /**
222  * @brief addPlanarCallout
223  *
224  * This function will add a json for planar callout in the input json list.
225  * The caller can pass this json list into createErrorPEL to apply the callout.
226  *
227  * @param[in,out] jsonCalloutDataList - json list where callout json will be
228  *                  emplaced
229  * @param[in] priority - string indicating priority.
230  */
231 static void addPlanarCallout(json& jsonCalloutDataList,
232                              const std::string& priority)
233 {
234     json jsonCalloutData;
235 
236     // Inventory path for planar
237     jsonCalloutData["InventoryPath"] =
238         "/xyz/openbmc_project/inventory/system/chassis/motherboard";
239     jsonCalloutData["Deconfigured"] = false;
240     jsonCalloutData["Guarded"] = false;
241     jsonCalloutData["Priority"] = priority;
242 
243     jsonCalloutDataList.emplace_back(jsonCalloutData);
244 }
245 
246 /**
247  * @brief processClockInfoErrorHelper
248  *
249  * Creates informational PEL for spare clock failure
250  *
251  * @param[in]  ffdc FFDC data capturd by the HWP
252  * @param[out] pelJSONFmtCalloutDataList used to store collected callout
253  *            data into pel expected format
254  * @param[out] ffdcUserData used to store additional ffdc user data to
255  *              provided by the SBE FFDC packet.
256  *
257  * @return NULL
258  *
259  **/
260 void processClockInfoErrorHelper(const FFDC& ffdc,
261                                  json& pelJSONFmtCalloutDataList,
262                                  FFDCData& ffdcUserData)
263 {
264     log<level::INFO>(
265         std::format("processClockInfoErrorHelper: FFDC Message[{}]",
266                     ffdc.message)
267             .c_str());
268 
269     // Adding hardware procedures return code details
270     ffdcUserData.emplace_back("HWP_RC", ffdc.hwp_errorinfo.rc);
271     ffdcUserData.emplace_back("HWP_RC_DESC", ffdc.hwp_errorinfo.rc_desc);
272 
273     // Adding hardware procedures required ffdc data for debug
274     for_each(ffdc.hwp_errorinfo.ffdcs_data.cbegin(),
275              ffdc.hwp_errorinfo.ffdcs_data.cend(),
276              [&ffdcUserData](
277                  const std::pair<std::string, std::string>& ele) -> void {
278         std::string keyWithPrefix("HWP_FFDC_");
279         keyWithPrefix.append(ele.first);
280 
281         ffdcUserData.emplace_back(keyWithPrefix, ele.second);
282     });
283     // get clock position information
284     auto clk_pos = 0xFF; // Invalid position.
285     for (auto& hwCallout : ffdc.hwp_errorinfo.hwcallouts)
286     {
287         if ((hwCallout.hwid == "PROC_REF_CLOCK") ||
288             (hwCallout.hwid == "PCI_REF_CLOCK"))
289         {
290             clk_pos = hwCallout.clkPos;
291             break;
292         }
293     }
294     // Adding CDG (Only deconfigure) targets details
295     for_each(ffdc.hwp_errorinfo.cdg_targets.begin(),
296              ffdc.hwp_errorinfo.cdg_targets.end(),
297              [&ffdcUserData, &pelJSONFmtCalloutDataList,
298               clk_pos](const CDG_Target& cdg_tgt) -> void {
299         json jsonCalloutData;
300         std::string pelPriority = "H";
301         jsonCalloutData["Priority"] = pelPriority; // Not used
302         jsonCalloutData["SymbolicFRU"] = "REFCLK" + std::to_string(clk_pos);
303         jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
304         jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path;
305         pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData);
306     });
307 }
308 
309 void convertFAPItoPELformat(FFDC& ffdc, json& pelJSONFmtCalloutDataList,
310                             FFDCData& ffdcUserData)
311 {
312     if (ffdc.ffdc_type == FFDC_TYPE_SPARE_CLOCK_INFO)
313     {
314         processClockInfoErrorHelper(ffdc, pelJSONFmtCalloutDataList,
315                                     ffdcUserData);
316         return;
317     }
318 
319     if (ffdc.ffdc_type == FFDC_TYPE_HWP)
320     {
321         // Adding hardware procedures return code details
322         ffdcUserData.emplace_back("HWP_RC", ffdc.hwp_errorinfo.rc);
323         ffdcUserData.emplace_back("HWP_RC_DESC", ffdc.hwp_errorinfo.rc_desc);
324 
325         // Adding hardware procedures required ffdc data for debug
326         for_each(
327             ffdc.hwp_errorinfo.ffdcs_data.begin(),
328             ffdc.hwp_errorinfo.ffdcs_data.end(),
329             [&ffdcUserData](std::pair<std::string, std::string>& ele) -> void {
330             std::string keyWithPrefix("HWP_FFDC_");
331             keyWithPrefix.append(ele.first);
332 
333             ffdcUserData.emplace_back(keyWithPrefix, ele.second);
334         });
335 
336         // Adding hardware callout details
337         int calloutCount = 0;
338         for_each(ffdc.hwp_errorinfo.hwcallouts.begin(),
339                  ffdc.hwp_errorinfo.hwcallouts.end(),
340                  [&ffdcUserData, &calloutCount, &pelJSONFmtCalloutDataList](
341                      const HWCallout& hwCallout) -> void {
342             calloutCount++;
343             std::stringstream keyPrefix;
344             keyPrefix << "HWP_HW_CO_" << std::setfill('0') << std::setw(2)
345                       << calloutCount << "_";
346 
347             ffdcUserData.emplace_back(
348                 std::string(keyPrefix.str()).append("HW_ID"), hwCallout.hwid);
349 
350             ffdcUserData.emplace_back(
351                 std::string(keyPrefix.str()).append("PRIORITY"),
352                 hwCallout.callout_priority);
353 
354             phal::TargetInfo targetInfo;
355             phal::getTgtReqAttrsVal(hwCallout.target_entity_path, targetInfo);
356 
357             std::string locationCode = std::string(targetInfo.locationCode);
358             ffdcUserData.emplace_back(
359                 std::string(keyPrefix.str()).append("LOC_CODE"), locationCode);
360 
361             std::string physPath = std::string(targetInfo.physDevPath);
362             ffdcUserData.emplace_back(
363                 std::string(keyPrefix.str()).append("PHYS_PATH"), physPath);
364 
365             ffdcUserData.emplace_back(
366                 std::string(keyPrefix.str()).append("CLK_POS"),
367                 std::to_string(hwCallout.clkPos));
368 
369             ffdcUserData.emplace_back(
370                 std::string(keyPrefix.str()).append("CALLOUT_PLANAR"),
371                 (hwCallout.isPlanarCallout == true ? "true" : "false"));
372 
373             std::string pelPriority =
374                 getPelPriority(hwCallout.callout_priority);
375 
376             if (hwCallout.isPlanarCallout)
377             {
378                 addPlanarCallout(pelJSONFmtCalloutDataList, pelPriority);
379             }
380         });
381 
382         // Adding CDG (callout, deconfigure and guard) targets details
383         calloutCount = 0;
384         for_each(ffdc.hwp_errorinfo.cdg_targets.begin(),
385                  ffdc.hwp_errorinfo.cdg_targets.end(),
386                  [&ffdcUserData, &calloutCount, &pelJSONFmtCalloutDataList](
387                      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"), locationCode);
401             std::string physPath = std::string(targetInfo.physDevPath);
402             ffdcUserData.emplace_back(
403                 std::string(keyPrefix.str()).append("PHYS_PATH"), physPath);
404 
405             ffdcUserData.emplace_back(
406                 std::string(keyPrefix.str()).append("CO_REQ"),
407                 (cdg_tgt.callout == true ? "true" : "false"));
408 
409             ffdcUserData.emplace_back(
410                 std::string(keyPrefix.str()).append("CO_PRIORITY"),
411                 cdg_tgt.callout_priority);
412 
413             ffdcUserData.emplace_back(
414                 std::string(keyPrefix.str()).append("DECONF_REQ"),
415                 (cdg_tgt.deconfigure == true ? "true" : "false"));
416 
417             ffdcUserData.emplace_back(
418                 std::string(keyPrefix.str()).append("GUARD_REQ"),
419                 (cdg_tgt.guard == true ? "true" : "false"));
420 
421             ffdcUserData.emplace_back(
422                 std::string(keyPrefix.str()).append("GUARD_TYPE"),
423                 cdg_tgt.guard_type);
424 
425             json jsonCalloutData;
426             jsonCalloutData["LocationCode"] = locationCode;
427             std::string pelPriority = 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') << std::setw(2)
453                       << 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>(std::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