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 
10 #include <algorithm>
11 #include <cstdlib>
12 #include <cstring>
13 #include <iomanip>
14 #include <list>
15 #include <map>
16 #include <phosphor-logging/elog.hpp>
17 #include <sstream>
18 #include <string>
19 
20 namespace openpower
21 {
22 namespace pels
23 {
24 namespace phal
25 {
26 
27 using namespace phosphor::logging;
28 
29 /**
30  * Used to pass buffer to pdbg callback api to get required target
31  * data (attributes) based on given data (attribute).
32  */
33 struct TargetInfo
34 {
35     ATTR_PHYS_BIN_PATH_Type physBinPath;
36     ATTR_LOCATION_CODE_Type locationCode;
37     ATTR_PHYS_DEV_PATH_Type physDevPath;
38     ATTR_MRU_ID_Type mruId;
39 
40     bool deconfigure;
41 
42     TargetInfo()
43     {
44         memset(&physBinPath, '\0', sizeof(physBinPath));
45         memset(&locationCode, '\0', sizeof(locationCode));
46         memset(&physDevPath, '\0', sizeof(physDevPath));
47         mruId = 0;
48         deconfigure = false;
49     }
50 };
51 
52 /**
53  * Used to return in callback function which are used to get
54  * physical path value and it binary format value.
55  *
56  * The value for constexpr defined based on pdbg_target_traverse function usage.
57  */
58 constexpr int continueTgtTraversal = 0;
59 constexpr int requireAttrFound = 1;
60 constexpr int requireAttrNotFound = 2;
61 
62 /**
63  * @brief Used to get target location code from phal device tree
64  *
65  * @param[in] target current device tree target
66  * @param[out] appPrivData used for accessing|storing from|to application
67  *
68  * @return 0 to continue traverse, non-zero to stop traverse
69  */
70 int pdbgCallbackToGetTgtReqAttrsVal(struct pdbg_target* target,
71                                     void* appPrivData)
72 {
73     TargetInfo* targetInfo = static_cast<TargetInfo*>(appPrivData);
74 
75     ATTR_PHYS_BIN_PATH_Type physBinPath;
76     /**
77      * TODO: Issue: phal/pdata#16
78      * Should not use direct pdbg api to read attribute. Need to use DT_GET_PROP
79      * macro for bmc app's and this will call libdt-api api but, it will print
80      * "pdbg_target_get_attribute failed" trace if attribute is not found and
81      * this callback will call recursively by using pdbg_target_traverse() until
82      * find expected attribute based on return code from this callback. Because,
83      * need to do target iteration to get actual attribute (ATTR_PHYS_BIN_PATH)
84      * value when device tree target info doesn't know to read attribute from
85      * device tree. So, Due to this error trace user will get confusion while
86      * looking traces. Hence using pdbg api to avoid trace until libdt-api
87      * provides log level setup.
88      */
89     if (!pdbg_target_get_attribute(
90             target, "ATTR_PHYS_BIN_PATH",
91             std::stoi(dtAttr::fapi2::ATTR_PHYS_BIN_PATH_Spec),
92             dtAttr::fapi2::ATTR_PHYS_BIN_PATH_ElementCount, physBinPath))
93     {
94         return continueTgtTraversal;
95     }
96 
97     if (std::memcmp(physBinPath, targetInfo->physBinPath,
98                     sizeof(physBinPath)) != 0)
99     {
100         return continueTgtTraversal;
101     }
102 
103     if (DT_GET_PROP(ATTR_LOCATION_CODE, target, targetInfo->locationCode))
104     {
105         log<level::ERR>("Could not read LOCATION_CODE attribute");
106         return requireAttrNotFound;
107     }
108 
109     if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, target, targetInfo->physDevPath))
110     {
111         log<level::ERR>("Could not read PHYS_DEV_PATH attribute");
112         return requireAttrNotFound;
113     }
114 
115     if (DT_GET_PROP(ATTR_MRU_ID, target, targetInfo->mruId))
116     {
117         log<level::ERR>("Could not read MRU_ID attribute");
118         return requireAttrNotFound;
119     }
120 
121     if (targetInfo->deconfigure)
122     {
123         ATTR_HWAS_STATE_Type hwasState;
124         if (DT_GET_PROP(ATTR_HWAS_STATE, target, hwasState))
125         {
126             log<level::ERR>("Could not read HWAS_STATE attribute");
127             return requireAttrNotFound;
128         }
129 
130         log<level::INFO>(fmt::format("Marking target({}) as Non-Functional",
131                                      targetInfo->physDevPath)
132                              .c_str());
133         hwasState.functional = 0;
134 
135         if (DT_SET_PROP(ATTR_HWAS_STATE, target, hwasState))
136         {
137             log<level::ERR>("Could not write HWAS_STATE attribute");
138             return requireAttrNotFound;
139         }
140     }
141 
142     return requireAttrFound;
143 }
144 
145 /**
146  * @brief Used to get target info (attributes data)
147  *
148  * To get target required attributes value using another attribute value
149  * ("PHYS_BIN_PATH" which is present in same target attributes list) by using
150  * "ipdbg_target_traverse" api because, here we have attribute value only and
151  * doesn't have respective device tree target info to get required attributes
152  * values from it attributes list.
153  *
154  * @param[in] physBinPath to pass PHYS_BIN_PATH value
155  * @param[out] targetInfo to pas buufer to fill with required attributes
156  *
157  * @return true on success otherwise false
158  */
159 bool getTgtReqAttrsVal(const std::vector<uint8_t>& physBinPath,
160                        TargetInfo& targetInfo)
161 {
162     std::memcpy(&targetInfo.physBinPath, physBinPath.data(),
163                 sizeof(targetInfo.physBinPath));
164 
165     int ret = pdbg_target_traverse(NULL, pdbgCallbackToGetTgtReqAttrsVal,
166                                    &targetInfo);
167     if (ret == 0)
168     {
169         log<level::ERR>(fmt::format("Given ATTR_PHYS_BIN_PATH value({}) "
170                                     "not found in phal device tree",
171                                     targetInfo.physBinPath)
172                             .c_str());
173         return false;
174     }
175     else if (ret == requireAttrNotFound)
176     {
177         return false;
178     }
179 
180     return true;
181 }
182 
183 /**
184  * @brief GET PEL priority from pHAL priority
185  *
186  * The pHAL callout priority is in different format than PEL format
187  * so, this api is used to return current phal supported priority into
188  * PEL expected format.
189  *
190  * @param[in] phalPriority used to pass phal priority format string
191  *
192  * @return pel priority format string else empty if failure
193  *
194  * @note For "NONE" returning "L" (LOW)
195  */
196 static std::string getPelPriority(const std::string& phalPriority)
197 {
198     const std::map<std::string, std::string> priorityMap = {
199         {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}};
200 
201     auto it = priorityMap.find(phalPriority);
202     if (it == priorityMap.end())
203     {
204         log<level::ERR>(fmt::format("Unsupported phal priority({}) is given "
205                                     "to get pel priority format",
206                                     phalPriority)
207                             .c_str());
208         return "H";
209     }
210 
211     return it->second;
212 }
213 
214 void convertFAPItoPELformat(FFDC& ffdc, json& pelJSONFmtCalloutDataList,
215                             FFDCData& ffdcUserData)
216 {
217     if (ffdc.ffdc_type == FFDC_TYPE_HWP)
218     {
219         // Adding hardware procedures return code details
220         ffdcUserData.emplace_back("HWP_RC", ffdc.hwp_errorinfo.rc);
221         ffdcUserData.emplace_back("HWP_RC_DESC", ffdc.hwp_errorinfo.rc_desc);
222 
223         // Adding hardware procedures required ffdc data for debug
224         for_each(
225             ffdc.hwp_errorinfo.ffdcs_data.begin(),
226             ffdc.hwp_errorinfo.ffdcs_data.end(),
227             [&ffdcUserData](std::pair<std::string, std::string>& ele) -> void {
228                 std::string keyWithPrefix("HWP_FFDC_");
229                 keyWithPrefix.append(ele.first);
230 
231                 ffdcUserData.emplace_back(keyWithPrefix, ele.second);
232             });
233 
234         // Adding hardware callout details
235         int calloutCount = 0;
236         for_each(
237             ffdc.hwp_errorinfo.hwcallouts.begin(),
238             ffdc.hwp_errorinfo.hwcallouts.end(),
239             [&ffdcUserData, &calloutCount,
240              &pelJSONFmtCalloutDataList](const HWCallout& hwCallout) -> void {
241                 calloutCount++;
242                 std::stringstream keyPrefix;
243                 keyPrefix << "HWP_HW_CO_" << std::setfill('0') << std::setw(2)
244                           << calloutCount << "_";
245 
246                 ffdcUserData.emplace_back(
247                     std::string(keyPrefix.str()).append("HW_ID"),
248                     hwCallout.hwid);
249 
250                 ffdcUserData.emplace_back(
251                     std::string(keyPrefix.str()).append("PRIORITY"),
252                     hwCallout.callout_priority);
253 
254                 phal::TargetInfo targetInfo;
255                 phal::getTgtReqAttrsVal(hwCallout.target_entity_path,
256                                         targetInfo);
257 
258                 std::string locationCode = std::string(targetInfo.locationCode);
259                 ffdcUserData.emplace_back(
260                     std::string(keyPrefix.str()).append("LOC_CODE"),
261                     locationCode);
262 
263                 std::string physPath = std::string(targetInfo.physDevPath);
264                 ffdcUserData.emplace_back(
265                     std::string(keyPrefix.str()).append("PHYS_PATH"), physPath);
266 
267                 ffdcUserData.emplace_back(
268                     std::string(keyPrefix.str()).append("CLK_POS"),
269                     std::to_string(hwCallout.clkPos));
270 
271                 json jsonCalloutData;
272                 jsonCalloutData["LocationCode"] = locationCode;
273                 std::string pelPriority =
274                     getPelPriority(hwCallout.callout_priority);
275                 jsonCalloutData["Priority"] = pelPriority;
276 
277                 if (targetInfo.mruId != 0)
278                 {
279                     jsonCalloutData["MRUs"] = json::array({
280                         {{"ID", targetInfo.mruId}, {"Priority", pelPriority}},
281                     });
282                 }
283 
284                 pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData);
285             });
286 
287         // Adding CDG (callout, deconfigure and guard) targets details
288         calloutCount = 0;
289         for_each(
290             ffdc.hwp_errorinfo.cdg_targets.begin(),
291             ffdc.hwp_errorinfo.cdg_targets.end(),
292             [&ffdcUserData, &calloutCount,
293              &pelJSONFmtCalloutDataList](const CDG_Target& cdg_tgt) -> void {
294                 calloutCount++;
295                 std::stringstream keyPrefix;
296                 keyPrefix << "HWP_CDG_TGT_" << std::setfill('0') << std::setw(2)
297                           << calloutCount << "_";
298 
299                 phal::TargetInfo targetInfo;
300                 targetInfo.deconfigure = cdg_tgt.deconfigure;
301 
302                 phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path, targetInfo);
303 
304                 std::string locationCode = std::string(targetInfo.locationCode);
305                 ffdcUserData.emplace_back(
306                     std::string(keyPrefix.str()).append("LOC_CODE"),
307                     locationCode);
308                 std::string physPath = std::string(targetInfo.physDevPath);
309                 ffdcUserData.emplace_back(
310                     std::string(keyPrefix.str()).append("PHYS_PATH"), physPath);
311 
312                 ffdcUserData.emplace_back(
313                     std::string(keyPrefix.str()).append("CO_REQ"),
314                     (cdg_tgt.callout == true ? "true" : "false"));
315 
316                 ffdcUserData.emplace_back(
317                     std::string(keyPrefix.str()).append("CO_PRIORITY"),
318                     cdg_tgt.callout_priority);
319 
320                 ffdcUserData.emplace_back(
321                     std::string(keyPrefix.str()).append("DECONF_REQ"),
322                     (cdg_tgt.deconfigure == true ? "true" : "false"));
323 
324                 ffdcUserData.emplace_back(
325                     std::string(keyPrefix.str()).append("GUARD_REQ"),
326                     (cdg_tgt.guard == true ? "true" : "false"));
327 
328                 ffdcUserData.emplace_back(
329                     std::string(keyPrefix.str()).append("GUARD_TYPE"),
330                     cdg_tgt.guard_type);
331 
332                 json jsonCalloutData;
333                 jsonCalloutData["LocationCode"] = locationCode;
334                 std::string pelPriority =
335                     getPelPriority(cdg_tgt.callout_priority);
336                 jsonCalloutData["Priority"] = pelPriority;
337 
338                 if (targetInfo.mruId != 0)
339                 {
340                     jsonCalloutData["MRUs"] = json::array({
341                         {{"ID", targetInfo.mruId}, {"Priority", pelPriority}},
342                     });
343                 }
344                 jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
345                 jsonCalloutData["Guarded"] = cdg_tgt.guard;
346 
347                 pelJSONFmtCalloutDataList.emplace_back(jsonCalloutData);
348             });
349     }
350     else if ((ffdc.ffdc_type != FFDC_TYPE_NONE) &&
351              (ffdc.ffdc_type != FFDC_TYPE_UNSUPPORTED))
352     {
353         log<level::ERR>(fmt::format("Unsupported phal FFDC type to create PEL. "
354                                     "MSG: {}",
355                                     ffdc.message)
356                             .c_str());
357     }
358 }
359 
360 } // namespace phal
361 } // namespace pels
362 } // namespace openpower
363