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 void convertFAPItoPELformat(FFDC& ffdc, json& pelJSONFmtCalloutDataList,
239                             FFDCData& ffdcUserData)
240 {
241     if (ffdc.ffdc_type == FFDC_TYPE_HWP)
242     {
243         // Adding hardware procedures return code details
244         ffdcUserData.emplace_back("HWP_RC", ffdc.hwp_errorinfo.rc);
245         ffdcUserData.emplace_back("HWP_RC_DESC", ffdc.hwp_errorinfo.rc_desc);
246 
247         // Adding hardware procedures required ffdc data for debug
248         for_each(
249             ffdc.hwp_errorinfo.ffdcs_data.begin(),
250             ffdc.hwp_errorinfo.ffdcs_data.end(),
251             [&ffdcUserData](std::pair<std::string, std::string>& ele) -> void {
252                 std::string keyWithPrefix("HWP_FFDC_");
253                 keyWithPrefix.append(ele.first);
254 
255                 ffdcUserData.emplace_back(keyWithPrefix, ele.second);
256             });
257 
258         // Adding hardware callout details
259         int calloutCount = 0;
260         for_each(
261             ffdc.hwp_errorinfo.hwcallouts.begin(),
262             ffdc.hwp_errorinfo.hwcallouts.end(),
263             [&ffdcUserData, &calloutCount,
264              &pelJSONFmtCalloutDataList](const HWCallout& hwCallout) -> void {
265                 calloutCount++;
266                 std::stringstream keyPrefix;
267                 keyPrefix << "HWP_HW_CO_" << std::setfill('0') << std::setw(2)
268                           << calloutCount << "_";
269 
270                 ffdcUserData.emplace_back(
271                     std::string(keyPrefix.str()).append("HW_ID"),
272                     hwCallout.hwid);
273 
274                 ffdcUserData.emplace_back(
275                     std::string(keyPrefix.str()).append("PRIORITY"),
276                     hwCallout.callout_priority);
277 
278                 phal::TargetInfo targetInfo;
279                 phal::getTgtReqAttrsVal(hwCallout.target_entity_path,
280                                         targetInfo);
281 
282                 std::string locationCode = std::string(targetInfo.locationCode);
283                 ffdcUserData.emplace_back(
284                     std::string(keyPrefix.str()).append("LOC_CODE"),
285                     locationCode);
286 
287                 std::string physPath = std::string(targetInfo.physDevPath);
288                 ffdcUserData.emplace_back(
289                     std::string(keyPrefix.str()).append("PHYS_PATH"), physPath);
290 
291                 ffdcUserData.emplace_back(
292                     std::string(keyPrefix.str()).append("CLK_POS"),
293                     std::to_string(hwCallout.clkPos));
294 
295                 ffdcUserData.emplace_back(
296                     std::string(keyPrefix.str()).append("CALLOUT_PLANAR"),
297                     (hwCallout.isPlanarCallout == true ? "true" : "false"));
298 
299                 std::string pelPriority =
300                     getPelPriority(hwCallout.callout_priority);
301 
302                 if (hwCallout.isPlanarCallout)
303                 {
304                     addPlanarCallout(pelJSONFmtCalloutDataList, pelPriority);
305                 }
306             });
307 
308         // Adding CDG (callout, deconfigure and guard) targets details
309         calloutCount = 0;
310         for_each(
311             ffdc.hwp_errorinfo.cdg_targets.begin(),
312             ffdc.hwp_errorinfo.cdg_targets.end(),
313             [&ffdcUserData, &calloutCount,
314              &pelJSONFmtCalloutDataList](const CDG_Target& cdg_tgt) -> void {
315                 calloutCount++;
316                 std::stringstream keyPrefix;
317                 keyPrefix << "HWP_CDG_TGT_" << std::setfill('0') << std::setw(2)
318                           << calloutCount << "_";
319 
320                 phal::TargetInfo targetInfo;
321                 targetInfo.deconfigure = cdg_tgt.deconfigure;
322 
323                 phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path, targetInfo);
324 
325                 std::string locationCode = std::string(targetInfo.locationCode);
326                 ffdcUserData.emplace_back(
327                     std::string(keyPrefix.str()).append("LOC_CODE"),
328                     locationCode);
329                 std::string physPath = std::string(targetInfo.physDevPath);
330                 ffdcUserData.emplace_back(
331                     std::string(keyPrefix.str()).append("PHYS_PATH"), physPath);
332 
333                 ffdcUserData.emplace_back(
334                     std::string(keyPrefix.str()).append("CO_REQ"),
335                     (cdg_tgt.callout == true ? "true" : "false"));
336 
337                 ffdcUserData.emplace_back(
338                     std::string(keyPrefix.str()).append("CO_PRIORITY"),
339                     cdg_tgt.callout_priority);
340 
341                 ffdcUserData.emplace_back(
342                     std::string(keyPrefix.str()).append("DECONF_REQ"),
343                     (cdg_tgt.deconfigure == true ? "true" : "false"));
344 
345                 ffdcUserData.emplace_back(
346                     std::string(keyPrefix.str()).append("GUARD_REQ"),
347                     (cdg_tgt.guard == true ? "true" : "false"));
348 
349                 ffdcUserData.emplace_back(
350                     std::string(keyPrefix.str()).append("GUARD_TYPE"),
351                     cdg_tgt.guard_type);
352 
353                 json jsonCalloutData;
354                 jsonCalloutData["LocationCode"] = locationCode;
355                 std::string pelPriority =
356                     getPelPriority(cdg_tgt.callout_priority);
357                 jsonCalloutData["Priority"] = pelPriority;
358 
359                 if (targetInfo.mruId != 0)
360                 {
361                     jsonCalloutData["MRUs"] = json::array({
362                         {{"ID", targetInfo.mruId}, {"Priority", pelPriority}},
363                     });
364                 }
365                 jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
366                 jsonCalloutData["Guarded"] = cdg_tgt.guard;
367                 jsonCalloutData["GuardType"] = cdg_tgt.guard_type;
368                 jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path;
369 
370                 pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData);
371             });
372 
373         // Adding procedure callout
374         calloutCount = 0;
375         for_each(ffdc.hwp_errorinfo.procedures_callout.begin(),
376                  ffdc.hwp_errorinfo.procedures_callout.end(),
377                  [&ffdcUserData, &calloutCount, &pelJSONFmtCalloutDataList](
378                      const ProcedureCallout& procCallout) -> void {
379                      calloutCount++;
380                      std::stringstream keyPrefix;
381                      keyPrefix << "HWP_PROC_CO_" << std::setfill('0')
382                                << std::setw(2) << calloutCount << "_";
383                      ffdcUserData.emplace_back(
384                          std::string(keyPrefix.str()).append("PRIORITY"),
385                          procCallout.callout_priority);
386                      ffdcUserData.emplace_back(
387                          std::string(keyPrefix.str()).append("MAINT_PROCEDURE"),
388                          procCallout.proc_callout);
389                      json jsonCalloutData;
390                      jsonCalloutData["Procedure"] = procCallout.proc_callout;
391                      std::string pelPriority =
392                          getPelPriority(procCallout.callout_priority);
393                      jsonCalloutData["Priority"] = pelPriority;
394                      pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData);
395                  });
396     }
397     else if ((ffdc.ffdc_type != FFDC_TYPE_NONE) &&
398              (ffdc.ffdc_type != FFDC_TYPE_UNSUPPORTED))
399     {
400         log<level::ERR>(fmt::format("Unsupported phal FFDC type to create PEL. "
401                                     "MSG: {}",
402                                     ffdc.message)
403                             .c_str());
404     }
405 }
406 
407 } // namespace phal
408 } // namespace pels
409 } // namespace openpower
410