blob: 8957487391332edc3109dd17e569dd67f4d7dae5 [file] [log] [blame]
Brad Bishopf6783cd2020-10-27 19:25:09 -04001extern "C" {
2#include <libpdbg.h>
3}
4
5#include "create_pel.hpp"
6#include "phal_error.hpp"
7
8#include <attributes_info.H>
9#include <fmt/format.h>
10#include <libekb.H>
11#include <libipl.H>
12
13#include <algorithm>
14#include <cstdlib>
15#include <cstring>
16#include <iomanip>
17#include <list>
18#include <map>
19#include <nlohmann/json.hpp>
20#include <phosphor-logging/elog.hpp>
21#include <sstream>
22#include <string>
23
24namespace openpower
25{
26namespace phal
27{
28using namespace phosphor::logging;
29
30/**
31 * Used to pass buffer to pdbg callback api to get required target
32 * data (attributes) based on given data (attribute).
33 */
34struct TargetInfo
35{
36 ATTR_PHYS_BIN_PATH_Type physBinPath;
37 ATTR_LOCATION_CODE_Type locationCode;
38 ATTR_PHYS_DEV_PATH_Type physDevPath;
39 ATTR_MRU_ID_Type mruId;
40
41 bool deconfigure;
42
43 TargetInfo()
44 {
45 memset(&physBinPath, '\0', sizeof(physBinPath));
46 memset(&locationCode, '\0', sizeof(locationCode));
47 memset(&physDevPath, '\0', sizeof(physDevPath));
48 mruId = 0;
49 deconfigure = false;
50 }
51};
52
53/**
54 * Used to return in callback function which are used to get
55 * physical path value and it binary format value.
56 *
57 * The value for constexpr defined based on pdbg_target_traverse function usage.
58 */
59constexpr int continueTgtTraversal = 0;
60constexpr int requireAttrFound = 1;
61constexpr int requireAttrNotFound = 2;
62
63/**
64 * @brief Used to get target location code from phal device tree
65 *
66 * @param[in] target current device tree target
67 * @param[out] appPrivData used for accessing|storing from|to application
68 *
69 * @return 0 to continue traverse, non-zero to stop traverse
70 */
71int pdbgCallbackToGetTgtReqAttrsVal(struct pdbg_target* target,
72 void* appPrivData)
73{
74 TargetInfo* targetInfo = static_cast<TargetInfo*>(appPrivData);
75
76 ATTR_PHYS_BIN_PATH_Type physBinPath;
77 /**
78 * TODO: Issue: phal/pdata#16
79 * Should not use direct pdbg api to read attribute. Need to use DT_GET_PROP
80 * macro for bmc app's and this will call libdt-api api but, it will print
81 * "pdbg_target_get_attribute failed" trace if attribute is not found and
82 * this callback will call recursively by using pdbg_target_traverse() until
83 * find expected attribute based on return code from this callback. Because,
84 * need to do target iteration to get actual attribute (ATTR_PHYS_BIN_PATH)
85 * value when device tree target info doesn't know to read attribute from
86 * device tree. So, Due to this error trace user will get confusion while
87 * looking traces. Hence using pdbg api to avoid trace until libdt-api
88 * provides log level setup.
89 */
90 if (!pdbg_target_get_attribute(
91 target, "ATTR_PHYS_BIN_PATH",
92 std::stoi(dtAttr::fapi2::ATTR_PHYS_BIN_PATH_Spec),
93 dtAttr::fapi2::ATTR_PHYS_BIN_PATH_ElementCount, physBinPath))
94 {
95 return continueTgtTraversal;
96 }
97
98 if (std::memcmp(physBinPath, targetInfo->physBinPath,
99 sizeof(physBinPath)) != 0)
100 {
101 return continueTgtTraversal;
102 }
103
104 if (DT_GET_PROP(ATTR_LOCATION_CODE, target, targetInfo->locationCode))
105 {
106 log<level::ERR>("Could not read LOCATION_CODE attribute");
107 return requireAttrNotFound;
108 }
109
110 if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, target, targetInfo->physDevPath))
111 {
112 log<level::ERR>("Could not read PHYS_DEV_PATH attribute");
113 return requireAttrNotFound;
114 }
115
116 if (DT_GET_PROP(ATTR_MRU_ID, target, targetInfo->mruId))
117 {
118 log<level::ERR>("Could not read MRU_ID attribute");
119 return requireAttrNotFound;
120 }
121
122 if (targetInfo->deconfigure)
123 {
124 ATTR_HWAS_STATE_Type hwasState;
125 if (DT_GET_PROP(ATTR_HWAS_STATE, target, hwasState))
126 {
127 log<level::ERR>("Could not read HWAS_STATE attribute");
128 return requireAttrNotFound;
129 }
130
131 log<level::INFO>(fmt::format("Marking target({}) as Non-Functional",
132 targetInfo->physDevPath)
133 .c_str());
134 hwasState.functional = 0;
135
136 if (DT_SET_PROP(ATTR_HWAS_STATE, target, hwasState))
137 {
138 log<level::ERR>("Could not write HWAS_STATE attribute");
139 return requireAttrNotFound;
140 }
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 */
160bool 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 log<level::ERR>(fmt::format("Given ATTR_PHYS_BIN_PATH value({}) "
171 "not found in phal device tree",
172 targetInfo.physBinPath)
173 .c_str());
174 return false;
175 }
176 else if (ret == requireAttrNotFound)
177 {
178 return false;
179 }
180
181 return true;
182}
183} // namespace phal
184
185namespace pel
186{
187using namespace phosphor::logging;
188
189namespace detail
190{
191using json = nlohmann::json;
192
193// keys need to be unique so using counter value to generate unique key
194static int counter = 0;
195
196// list of debug traces
197static std::vector<std::pair<std::string, std::string>> traceLog;
198
199void processLogTraceCallback(void* private_data, const char* fmt, va_list ap)
200{
201 va_list vap;
202 va_copy(vap, ap);
203 std::vector<char> logData(1 + std::vsnprintf(nullptr, 0, fmt, ap));
204 std::vsnprintf(logData.data(), logData.size(), fmt, vap);
205 va_end(vap);
206 std::string logstr(logData.begin(), logData.end());
207
208 log<level::INFO>(logstr.c_str());
209
210 char timeBuf[80];
211 time_t t = time(0);
212 tm myTm{};
213 gmtime_r(&t, &myTm);
214 strftime(timeBuf, 80, "%Y-%m-%d %H:%M:%S", &myTm);
215
216 // key values need to be unique for PEL
217 // TODO #openbmc/dev/issues/1563
218 // If written to Json no need to worry about unique KEY
219 std::stringstream str;
220 str << std::setfill('0');
221 str << "LOG" << std::setw(3) << counter;
222 str << " " << timeBuf;
223 traceLog.emplace_back(std::make_pair(str.str(), std::move(logstr)));
224 counter++;
225}
226
227/**
228 * @brief GET PEL priority from pHAL priority
229 *
230 * The pHAL callout priority is in different format than PEL format
231 * so, this api is used to return current phal supported priority into
232 * PEL expected format.
233 *
234 * @param[in] phalPriority used to pass phal priority format string
235 *
236 * @return pel priority format string else empty if failure
237 *
238 * @note For "NONE" returning "L" (LOW)
239 */
240static std::string getPelPriority(const std::string& phalPriority)
241{
242 const std::map<std::string, std::string> priorityMap = {
243 {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}};
244
245 auto it = priorityMap.find(phalPriority);
246 if (it == priorityMap.end())
247 {
248 log<level::ERR>(fmt::format("Unsupported phal priority({}) is given "
249 "to get pel priority format",
250 phalPriority)
251 .c_str());
252 return "H";
253 }
254
255 return it->second;
256}
257
258void processBootErrorCallback(bool status)
259{
260 log<level::INFO>("processBootCallback ", entry("STATUS=%d", status));
261 try
262 {
263 // return If no failure during hwp execution
264 if (status)
265 return;
266
267 // Collecting ffdc details from phal
268 FFDC ffdc;
269 libekb_get_ffdc(ffdc);
270
271 log<level::INFO>(fmt::format("Collected pHAL FFDC. "
272 "MSG: {}",
273 ffdc.message)
274 .c_str());
275
276 // To store callouts details in json format as per pel expectation.
277 json jsonCalloutDataList;
278 jsonCalloutDataList = json::array();
279
280 // To store phal trace and other additional data about ffdc.
281 FFDCData pelAdditionalData;
282
283 if (ffdc.ffdc_type == FFDC_TYPE_HWP)
284 {
285 // Adding hardware procedures return code details
286 pelAdditionalData.emplace_back("HWP_RC", ffdc.hwp_errorinfo.rc);
287 pelAdditionalData.emplace_back("HWP_RC_DESC",
288 ffdc.hwp_errorinfo.rc_desc);
289
290 // Adding hardware procedures required ffdc data for debug
291 for_each(ffdc.hwp_errorinfo.ffdcs_data.begin(),
292 ffdc.hwp_errorinfo.ffdcs_data.end(),
293 [&pelAdditionalData](
294 std::pair<std::string, std::string>& ele) -> void {
295 std::string keyWithPrefix("HWP_FFDC_");
296 keyWithPrefix.append(ele.first);
297
298 pelAdditionalData.emplace_back(keyWithPrefix,
299 ele.second);
300 });
301
302 // Adding hardware callout details
303 int calloutCount = 0;
304 for_each(ffdc.hwp_errorinfo.hwcallouts.begin(),
305 ffdc.hwp_errorinfo.hwcallouts.end(),
306 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList](
307 const HWCallout& hwCallout) -> void {
308 calloutCount++;
309 std::stringstream keyPrefix;
310 keyPrefix << "HWP_HW_CO_" << std::setfill('0')
311 << std::setw(2) << calloutCount << "_";
312
313 pelAdditionalData.emplace_back(
314 std::string(keyPrefix.str()).append("HW_ID"),
315 hwCallout.hwid);
316
317 pelAdditionalData.emplace_back(
318 std::string(keyPrefix.str()).append("PRIORITY"),
319 hwCallout.callout_priority);
320
321 phal::TargetInfo targetInfo;
322 phal::getTgtReqAttrsVal(hwCallout.target_entity_path,
323 targetInfo);
324
325 std::string locationCode =
326 std::string(targetInfo.locationCode);
327 pelAdditionalData.emplace_back(
328 std::string(keyPrefix.str()).append("LOC_CODE"),
329 locationCode);
330
331 std::string physPath =
332 std::string(targetInfo.physDevPath);
333 pelAdditionalData.emplace_back(
334 std::string(keyPrefix.str()).append("PHYS_PATH"),
335 physPath);
336
337 pelAdditionalData.emplace_back(
338 std::string(keyPrefix.str()).append("CLK_POS"),
339 std::to_string(hwCallout.clkPos));
340
341 json jsonCalloutData;
342 jsonCalloutData["LocationCode"] = locationCode;
343 std::string pelPriority =
344 getPelPriority(hwCallout.callout_priority);
345 jsonCalloutData["Priority"] = pelPriority;
346
347 if (targetInfo.mruId != 0)
348 {
349 jsonCalloutData["MRUs"] = json::array({
350 {{"ID", targetInfo.mruId},
351 {"Priority", pelPriority}},
352 });
353 }
354
355 jsonCalloutDataList.emplace_back(jsonCalloutData);
356 });
357
358 // Adding CDG (callout, deconfigure and guard) targets details
359 calloutCount = 0;
360 for_each(ffdc.hwp_errorinfo.cdg_targets.begin(),
361 ffdc.hwp_errorinfo.cdg_targets.end(),
362 [&pelAdditionalData, &calloutCount,
363 &jsonCalloutDataList](const CDG_Target& cdg_tgt) -> void {
364 calloutCount++;
365 std::stringstream keyPrefix;
366 keyPrefix << "HWP_CDG_TGT_" << std::setfill('0')
367 << std::setw(2) << calloutCount << "_";
368
369 phal::TargetInfo targetInfo;
370 targetInfo.deconfigure = cdg_tgt.deconfigure;
371
372 phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path,
373 targetInfo);
374
375 std::string locationCode =
376 std::string(targetInfo.locationCode);
377 pelAdditionalData.emplace_back(
378 std::string(keyPrefix.str()).append("LOC_CODE"),
379 locationCode);
380 std::string physPath =
381 std::string(targetInfo.physDevPath);
382 pelAdditionalData.emplace_back(
383 std::string(keyPrefix.str()).append("PHYS_PATH"),
384 physPath);
385
386 pelAdditionalData.emplace_back(
387 std::string(keyPrefix.str()).append("CO_REQ"),
388 (cdg_tgt.callout == true ? "true" : "false"));
389
390 pelAdditionalData.emplace_back(
391 std::string(keyPrefix.str()).append("CO_PRIORITY"),
392 cdg_tgt.callout_priority);
393
394 pelAdditionalData.emplace_back(
395 std::string(keyPrefix.str()).append("DECONF_REQ"),
396 (cdg_tgt.deconfigure == true ? "true" : "false"));
397
398 pelAdditionalData.emplace_back(
399 std::string(keyPrefix.str()).append("GUARD_REQ"),
400 (cdg_tgt.guard == true ? "true" : "false"));
401
402 pelAdditionalData.emplace_back(
403 std::string(keyPrefix.str()).append("GUARD_TYPE"),
404 cdg_tgt.guard_type);
405
406 json jsonCalloutData;
407 jsonCalloutData["LocationCode"] = locationCode;
408 std::string pelPriority =
409 getPelPriority(cdg_tgt.callout_priority);
410 jsonCalloutData["Priority"] = pelPriority;
411
412 if (targetInfo.mruId != 0)
413 {
414 jsonCalloutData["MRUs"] = json::array({
415 {{"ID", targetInfo.mruId},
416 {"Priority", pelPriority}},
417 });
418 }
419 jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
420 jsonCalloutData["Guarded"] = cdg_tgt.guard;
421
422 jsonCalloutDataList.emplace_back(jsonCalloutData);
423 });
424 }
425 else if ((ffdc.ffdc_type != FFDC_TYPE_NONE) ||
426 (ffdc.ffdc_type != FFDC_TYPE_UNSUPPORTED))
427 {
428 log<level::ERR>(
429 fmt::format("Unsupported phal FFDC type to create PEL. "
430 "MSG: {}",
431 ffdc.message)
432 .c_str());
433 }
434
435 // Adding collected phal logs into PEL additional data
436 for_each(traceLog.begin(), traceLog.end(),
437 [&pelAdditionalData](
438 std::pair<std::string, std::string>& ele) -> void {
439 pelAdditionalData.emplace_back(ele.first, ele.second);
440 });
441
442 // TODO: #ibm-openbmc/dev/issues/2595 : Once enabled this support,
443 // callout details is not required to sort in H,M and L orders which
444 // are expected by pel because, pel will take care for sorting callouts
445 // based on priority so, now adding support to send callout in order
446 // i.e High -> Medium -> Low.
447 std::sort(
448 jsonCalloutDataList.begin(), jsonCalloutDataList.end(),
449 [](const json& aEle, const json& bEle) -> bool {
450 // Considering b element having higher priority than a element
451 // or Both element will be same priorty (to keep same order
452 // which are given by phal when two callouts are having same
453 // priority)
454 if (((aEle["Priority"] == "M") && (bEle["Priority"] == "H")) ||
455 ((aEle["Priority"] == "L") &&
456 ((bEle["Priority"] == "H") ||
457 (bEle["Priority"] == "M"))) ||
458 (aEle["Priority"] == bEle["Priority"]))
459 {
460 return false;
461 }
462
463 // Considering a element having higher priority than b element
464 return true;
465 });
466
467 openpower::pel::createBootErrorPEL(pelAdditionalData,
468 jsonCalloutDataList);
469 }
470 catch (std::exception& ex)
471 {
472 reset();
473 throw ex;
474 }
475 reset();
476}
477
478void reset()
479{
480 // reset the trace log and counter
481 traceLog.clear();
482 counter = 0;
483}
484
485void pDBGLogTraceCallbackHelper(int log_level, const char* fmt, va_list ap)
486{
487 processLogTraceCallback(NULL, fmt, ap);
488}
489} // namespace detail
490
491static inline uint8_t getLogLevelFromEnv(const char* env, const uint8_t dValue)
492{
493 auto logLevel = dValue;
494 try
495 {
496 if (const char* env_p = std::getenv(env))
497 {
498 logLevel = std::stoi(env_p);
499 }
500 }
501 catch (std::exception& e)
502 {
503 log<level::ERR>(("Conversion Failure"), entry("ENVIRONMENT=%s", env),
504 entry("EXCEPTION=%s", e.what()));
505 }
506 return logLevel;
507}
508
509void addBootErrorCallbacks()
510{
511 // Get individual phal repos log level from environment variable
512 // and update the log level.
513 pdbg_set_loglevel(getLogLevelFromEnv("PDBG_LOG", PDBG_INFO));
514 libekb_set_loglevel(getLogLevelFromEnv("LIBEKB_LOG", LIBEKB_LOG_IMP));
515 ipl_set_loglevel(getLogLevelFromEnv("IPL_LOG", IPL_INFO));
516
517 // add callback for debug traces
518 pdbg_set_logfunc(detail::pDBGLogTraceCallbackHelper);
519 libekb_set_logfunc(detail::processLogTraceCallback, NULL);
520 ipl_set_logfunc(detail::processLogTraceCallback, NULL);
521
522 // add callback for ipl failures
523 ipl_set_error_callback_func(detail::processBootErrorCallback);
524}
525
526} // namespace pel
527} // namespace openpower