blob: b6dc1d784dcca68a259ec30cc3c3f3d8f59085c9 [file] [log] [blame]
Brad Bishop5e5d4452020-10-27 19:46:13 -04001extern "C"
2{
Brad Bishopf6783cd2020-10-27 19:25:09 -04003#include <libpdbg.h>
4}
5
6#include "create_pel.hpp"
Jayanth Othayoth4079f092021-09-20 07:36:54 -05007#include "extensions/phal/common_utils.hpp"
Brad Bishopf6783cd2020-10-27 19:25:09 -04008#include "phal_error.hpp"
9
10#include <attributes_info.H>
11#include <fmt/format.h>
12#include <libekb.H>
Jayanth Othayoth4079f092021-09-20 07:36:54 -050013#include <libphal.H>
Brad Bishopf6783cd2020-10-27 19:25:09 -040014
Brad Bishop5e5d4452020-10-27 19:46:13 -040015#include <nlohmann/json.hpp>
16#include <phosphor-logging/elog.hpp>
17
Brad Bishopf6783cd2020-10-27 19:25:09 -040018#include <algorithm>
19#include <cstdlib>
20#include <cstring>
21#include <iomanip>
22#include <list>
23#include <map>
Brad Bishopf6783cd2020-10-27 19:25:09 -040024#include <sstream>
25#include <string>
26
27namespace openpower
28{
29namespace phal
30{
31using namespace phosphor::logging;
32
33/**
34 * Used to pass buffer to pdbg callback api to get required target
35 * data (attributes) based on given data (attribute).
36 */
37struct TargetInfo
38{
39 ATTR_PHYS_BIN_PATH_Type physBinPath;
40 ATTR_LOCATION_CODE_Type locationCode;
41 ATTR_PHYS_DEV_PATH_Type physDevPath;
42 ATTR_MRU_ID_Type mruId;
43
44 bool deconfigure;
45
46 TargetInfo()
47 {
48 memset(&physBinPath, '\0', sizeof(physBinPath));
49 memset(&locationCode, '\0', sizeof(locationCode));
50 memset(&physDevPath, '\0', sizeof(physDevPath));
51 mruId = 0;
52 deconfigure = false;
53 }
54};
55
56/**
57 * Used to return in callback function which are used to get
58 * physical path value and it binary format value.
59 *
60 * The value for constexpr defined based on pdbg_target_traverse function usage.
61 */
62constexpr int continueTgtTraversal = 0;
63constexpr int requireAttrFound = 1;
64constexpr int requireAttrNotFound = 2;
65
66/**
67 * @brief Used to get target location code from phal device tree
68 *
69 * @param[in] target current device tree target
70 * @param[out] appPrivData used for accessing|storing from|to application
71 *
72 * @return 0 to continue traverse, non-zero to stop traverse
73 */
74int pdbgCallbackToGetTgtReqAttrsVal(struct pdbg_target* target,
75 void* appPrivData)
76{
77 TargetInfo* targetInfo = static_cast<TargetInfo*>(appPrivData);
78
79 ATTR_PHYS_BIN_PATH_Type physBinPath;
80 /**
81 * TODO: Issue: phal/pdata#16
82 * Should not use direct pdbg api to read attribute. Need to use DT_GET_PROP
83 * macro for bmc app's and this will call libdt-api api but, it will print
84 * "pdbg_target_get_attribute failed" trace if attribute is not found and
85 * this callback will call recursively by using pdbg_target_traverse() until
86 * find expected attribute based on return code from this callback. Because,
87 * need to do target iteration to get actual attribute (ATTR_PHYS_BIN_PATH)
88 * value when device tree target info doesn't know to read attribute from
89 * device tree. So, Due to this error trace user will get confusion while
90 * looking traces. Hence using pdbg api to avoid trace until libdt-api
91 * provides log level setup.
92 */
93 if (!pdbg_target_get_attribute(
94 target, "ATTR_PHYS_BIN_PATH",
95 std::stoi(dtAttr::fapi2::ATTR_PHYS_BIN_PATH_Spec),
96 dtAttr::fapi2::ATTR_PHYS_BIN_PATH_ElementCount, physBinPath))
97 {
98 return continueTgtTraversal;
99 }
100
101 if (std::memcmp(physBinPath, targetInfo->physBinPath,
102 sizeof(physBinPath)) != 0)
103 {
104 return continueTgtTraversal;
105 }
106
107 if (DT_GET_PROP(ATTR_LOCATION_CODE, target, targetInfo->locationCode))
108 {
109 log<level::ERR>("Could not read LOCATION_CODE attribute");
110 return requireAttrNotFound;
111 }
112
113 if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, target, targetInfo->physDevPath))
114 {
115 log<level::ERR>("Could not read PHYS_DEV_PATH attribute");
116 return requireAttrNotFound;
117 }
118
119 if (DT_GET_PROP(ATTR_MRU_ID, target, targetInfo->mruId))
120 {
121 log<level::ERR>("Could not read MRU_ID attribute");
122 return requireAttrNotFound;
123 }
124
125 if (targetInfo->deconfigure)
126 {
127 ATTR_HWAS_STATE_Type hwasState;
128 if (DT_GET_PROP(ATTR_HWAS_STATE, target, hwasState))
129 {
130 log<level::ERR>("Could not read HWAS_STATE attribute");
131 return requireAttrNotFound;
132 }
133
134 log<level::INFO>(fmt::format("Marking target({}) as Non-Functional",
135 targetInfo->physDevPath)
136 .c_str());
137 hwasState.functional = 0;
138
139 if (DT_SET_PROP(ATTR_HWAS_STATE, target, hwasState))
140 {
141 log<level::ERR>("Could not write HWAS_STATE attribute");
142 return requireAttrNotFound;
143 }
144 }
145
146 return requireAttrFound;
147}
148
149/**
150 * @brief Used to get target info (attributes data)
151 *
152 * To get target required attributes value using another attribute value
153 * ("PHYS_BIN_PATH" which is present in same target attributes list) by using
154 * "ipdbg_target_traverse" api because, here we have attribute value only and
155 * doesn't have respective device tree target info to get required attributes
156 * values from it attributes list.
157 *
158 * @param[in] physBinPath to pass PHYS_BIN_PATH value
159 * @param[out] targetInfo to pas buufer to fill with required attributes
160 *
161 * @return true on success otherwise false
162 */
163bool getTgtReqAttrsVal(const std::vector<uint8_t>& physBinPath,
164 TargetInfo& targetInfo)
165{
166 std::memcpy(&targetInfo.physBinPath, physBinPath.data(),
167 sizeof(targetInfo.physBinPath));
168
169 int ret = pdbg_target_traverse(NULL, pdbgCallbackToGetTgtReqAttrsVal,
170 &targetInfo);
171 if (ret == 0)
172 {
173 log<level::ERR>(fmt::format("Given ATTR_PHYS_BIN_PATH value({}) "
174 "not found in phal device tree",
175 targetInfo.physBinPath)
176 .c_str());
177 return false;
178 }
179 else if (ret == requireAttrNotFound)
180 {
181 return false;
182 }
183
184 return true;
185}
186} // namespace phal
187
188namespace pel
189{
190using namespace phosphor::logging;
191
192namespace detail
193{
194using json = nlohmann::json;
195
196// keys need to be unique so using counter value to generate unique key
197static int counter = 0;
198
199// list of debug traces
200static std::vector<std::pair<std::string, std::string>> traceLog;
201
Brad Bishop63508a72020-10-27 18:55:01 -0400202void processLogTraceCallback(void*, const char* fmt, va_list ap)
Brad Bishopf6783cd2020-10-27 19:25:09 -0400203{
204 va_list vap;
205 va_copy(vap, ap);
206 std::vector<char> logData(1 + std::vsnprintf(nullptr, 0, fmt, ap));
207 std::vsnprintf(logData.data(), logData.size(), fmt, vap);
208 va_end(vap);
209 std::string logstr(logData.begin(), logData.end());
210
211 log<level::INFO>(logstr.c_str());
212
213 char timeBuf[80];
214 time_t t = time(0);
215 tm myTm{};
216 gmtime_r(&t, &myTm);
217 strftime(timeBuf, 80, "%Y-%m-%d %H:%M:%S", &myTm);
218
219 // key values need to be unique for PEL
220 // TODO #openbmc/dev/issues/1563
221 // If written to Json no need to worry about unique KEY
222 std::stringstream str;
223 str << std::setfill('0');
224 str << "LOG" << std::setw(3) << counter;
225 str << " " << timeBuf;
226 traceLog.emplace_back(std::make_pair(str.str(), std::move(logstr)));
227 counter++;
228}
229
230/**
231 * @brief GET PEL priority from pHAL priority
232 *
233 * The pHAL callout priority is in different format than PEL format
234 * so, this api is used to return current phal supported priority into
235 * PEL expected format.
236 *
237 * @param[in] phalPriority used to pass phal priority format string
238 *
239 * @return pel priority format string else empty if failure
240 *
241 * @note For "NONE" returning "L" (LOW)
242 */
243static std::string getPelPriority(const std::string& phalPriority)
244{
245 const std::map<std::string, std::string> priorityMap = {
246 {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}};
247
248 auto it = priorityMap.find(phalPriority);
249 if (it == priorityMap.end())
250 {
251 log<level::ERR>(fmt::format("Unsupported phal priority({}) is given "
252 "to get pel priority format",
253 phalPriority)
254 .c_str());
255 return "H";
256 }
257
258 return it->second;
259}
260
Jayanth Othayoth2b211702021-09-06 05:14:23 -0500261void processIplErrorCallback(const ipl_error_info& errInfo)
Brad Bishopf6783cd2020-10-27 19:25:09 -0400262{
Jayanth Othayoth2b211702021-09-06 05:14:23 -0500263 log<level::INFO>(
Jayanth Othayoth4079f092021-09-20 07:36:54 -0500264 fmt::format("processIplErrorCallback: Error type({})", errInfo.type)
Jayanth Othayoth2b211702021-09-06 05:14:23 -0500265 .c_str());
266
Jayanth Othayothee56c552021-09-10 01:44:05 -0500267 if (errInfo.type == IPL_ERR_OK)
Jayanth Othayoth2b211702021-09-06 05:14:23 -0500268 {
269 // reset trace log and exit
270 reset();
271 return;
272 }
Jayanth Othayoth4079f092021-09-20 07:36:54 -0500273
274 if (errInfo.type == IPL_ERR_SBE_BOOT)
275 {
276 processSbeBootError();
277 return;
278 }
279
Jayanth Othayoth2b211702021-09-06 05:14:23 -0500280 // TODO: Keeping the existing behaviour now
281 // Handle errors based on special reason codes once support is available
282 processBootError(false);
283}
284
285void processBootError(bool status)
286{
287 log<level::INFO>("processBootError ", entry("STATUS=%d", status));
Brad Bishopf6783cd2020-10-27 19:25:09 -0400288 try
289 {
290 // return If no failure during hwp execution
291 if (status)
292 return;
293
294 // Collecting ffdc details from phal
295 FFDC ffdc;
296 libekb_get_ffdc(ffdc);
297
Ramesh Iyyar3af83eb2020-11-19 23:11:38 -0600298 log<level::INFO>(
299 fmt::format("PHAL FFDC: Return Message[{}]", ffdc.message).c_str());
Brad Bishopf6783cd2020-10-27 19:25:09 -0400300
301 // To store callouts details in json format as per pel expectation.
302 json jsonCalloutDataList;
303 jsonCalloutDataList = json::array();
304
305 // To store phal trace and other additional data about ffdc.
306 FFDCData pelAdditionalData;
307
308 if (ffdc.ffdc_type == FFDC_TYPE_HWP)
309 {
310 // Adding hardware procedures return code details
311 pelAdditionalData.emplace_back("HWP_RC", ffdc.hwp_errorinfo.rc);
312 pelAdditionalData.emplace_back("HWP_RC_DESC",
313 ffdc.hwp_errorinfo.rc_desc);
314
315 // Adding hardware procedures required ffdc data for debug
316 for_each(ffdc.hwp_errorinfo.ffdcs_data.begin(),
317 ffdc.hwp_errorinfo.ffdcs_data.end(),
318 [&pelAdditionalData](
319 std::pair<std::string, std::string>& ele) -> void {
320 std::string keyWithPrefix("HWP_FFDC_");
321 keyWithPrefix.append(ele.first);
322
323 pelAdditionalData.emplace_back(keyWithPrefix,
324 ele.second);
325 });
326
327 // Adding hardware callout details
328 int calloutCount = 0;
329 for_each(ffdc.hwp_errorinfo.hwcallouts.begin(),
330 ffdc.hwp_errorinfo.hwcallouts.end(),
331 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList](
332 const HWCallout& hwCallout) -> void {
333 calloutCount++;
334 std::stringstream keyPrefix;
335 keyPrefix << "HWP_HW_CO_" << std::setfill('0')
336 << std::setw(2) << calloutCount << "_";
337
338 pelAdditionalData.emplace_back(
339 std::string(keyPrefix.str()).append("HW_ID"),
340 hwCallout.hwid);
341
342 pelAdditionalData.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,
348 targetInfo);
349
350 std::string locationCode =
351 std::string(targetInfo.locationCode);
352 pelAdditionalData.emplace_back(
353 std::string(keyPrefix.str()).append("LOC_CODE"),
354 locationCode);
355
356 std::string physPath =
357 std::string(targetInfo.physDevPath);
358 pelAdditionalData.emplace_back(
359 std::string(keyPrefix.str()).append("PHYS_PATH"),
360 physPath);
361
362 pelAdditionalData.emplace_back(
363 std::string(keyPrefix.str()).append("CLK_POS"),
364 std::to_string(hwCallout.clkPos));
365
366 json jsonCalloutData;
367 jsonCalloutData["LocationCode"] = locationCode;
368 std::string pelPriority =
369 getPelPriority(hwCallout.callout_priority);
370 jsonCalloutData["Priority"] = pelPriority;
371
372 if (targetInfo.mruId != 0)
373 {
374 jsonCalloutData["MRUs"] = json::array({
375 {{"ID", targetInfo.mruId},
376 {"Priority", pelPriority}},
377 });
378 }
379
380 jsonCalloutDataList.emplace_back(jsonCalloutData);
381 });
382
383 // Adding CDG (callout, deconfigure and guard) targets details
384 calloutCount = 0;
385 for_each(ffdc.hwp_errorinfo.cdg_targets.begin(),
386 ffdc.hwp_errorinfo.cdg_targets.end(),
387 [&pelAdditionalData, &calloutCount,
388 &jsonCalloutDataList](const CDG_Target& cdg_tgt) -> void {
389 calloutCount++;
390 std::stringstream keyPrefix;
391 keyPrefix << "HWP_CDG_TGT_" << std::setfill('0')
392 << std::setw(2) << calloutCount << "_";
393
394 phal::TargetInfo targetInfo;
395 targetInfo.deconfigure = cdg_tgt.deconfigure;
396
397 phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path,
398 targetInfo);
399
400 std::string locationCode =
401 std::string(targetInfo.locationCode);
402 pelAdditionalData.emplace_back(
403 std::string(keyPrefix.str()).append("LOC_CODE"),
404 locationCode);
405 std::string physPath =
406 std::string(targetInfo.physDevPath);
407 pelAdditionalData.emplace_back(
408 std::string(keyPrefix.str()).append("PHYS_PATH"),
409 physPath);
410
411 pelAdditionalData.emplace_back(
412 std::string(keyPrefix.str()).append("CO_REQ"),
413 (cdg_tgt.callout == true ? "true" : "false"));
414
415 pelAdditionalData.emplace_back(
416 std::string(keyPrefix.str()).append("CO_PRIORITY"),
417 cdg_tgt.callout_priority);
418
419 pelAdditionalData.emplace_back(
420 std::string(keyPrefix.str()).append("DECONF_REQ"),
421 (cdg_tgt.deconfigure == true ? "true" : "false"));
422
423 pelAdditionalData.emplace_back(
424 std::string(keyPrefix.str()).append("GUARD_REQ"),
425 (cdg_tgt.guard == true ? "true" : "false"));
426
427 pelAdditionalData.emplace_back(
428 std::string(keyPrefix.str()).append("GUARD_TYPE"),
429 cdg_tgt.guard_type);
430
431 json jsonCalloutData;
432 jsonCalloutData["LocationCode"] = locationCode;
433 std::string pelPriority =
434 getPelPriority(cdg_tgt.callout_priority);
435 jsonCalloutData["Priority"] = pelPriority;
436
437 if (targetInfo.mruId != 0)
438 {
439 jsonCalloutData["MRUs"] = json::array({
440 {{"ID", targetInfo.mruId},
441 {"Priority", pelPriority}},
442 });
443 }
444 jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
445 jsonCalloutData["Guarded"] = cdg_tgt.guard;
446
447 jsonCalloutDataList.emplace_back(jsonCalloutData);
448 });
449 }
Ramesh Iyyar3af83eb2020-11-19 23:11:38 -0600450 else if ((ffdc.ffdc_type != FFDC_TYPE_NONE) &&
Brad Bishopf6783cd2020-10-27 19:25:09 -0400451 (ffdc.ffdc_type != FFDC_TYPE_UNSUPPORTED))
452 {
453 log<level::ERR>(
454 fmt::format("Unsupported phal FFDC type to create PEL. "
455 "MSG: {}",
456 ffdc.message)
457 .c_str());
458 }
459
460 // Adding collected phal logs into PEL additional data
461 for_each(traceLog.begin(), traceLog.end(),
462 [&pelAdditionalData](
463 std::pair<std::string, std::string>& ele) -> void {
464 pelAdditionalData.emplace_back(ele.first, ele.second);
465 });
466
467 // TODO: #ibm-openbmc/dev/issues/2595 : Once enabled this support,
468 // callout details is not required to sort in H,M and L orders which
469 // are expected by pel because, pel will take care for sorting callouts
470 // based on priority so, now adding support to send callout in order
471 // i.e High -> Medium -> Low.
472 std::sort(
473 jsonCalloutDataList.begin(), jsonCalloutDataList.end(),
474 [](const json& aEle, const json& bEle) -> bool {
475 // Considering b element having higher priority than a element
476 // or Both element will be same priorty (to keep same order
477 // which are given by phal when two callouts are having same
478 // priority)
479 if (((aEle["Priority"] == "M") && (bEle["Priority"] == "H")) ||
480 ((aEle["Priority"] == "L") &&
481 ((bEle["Priority"] == "H") ||
482 (bEle["Priority"] == "M"))) ||
483 (aEle["Priority"] == bEle["Priority"]))
484 {
485 return false;
486 }
487
488 // Considering a element having higher priority than b element
489 return true;
490 });
491
492 openpower::pel::createBootErrorPEL(pelAdditionalData,
493 jsonCalloutDataList);
494 }
Patrick Williams1a9a5a62021-10-06 13:05:06 -0500495 catch (const std::exception& ex)
Brad Bishopf6783cd2020-10-27 19:25:09 -0400496 {
497 reset();
498 throw ex;
499 }
500 reset();
501}
502
Jayanth Othayoth4079f092021-09-20 07:36:54 -0500503void processSbeBootError()
504{
505 log<level::INFO>("processSbeBootError : Entered ");
506
507 using namespace openpower::phal::sbe;
508 using namespace openpower::phal::exception;
509
510 // To store phal trace and other additional data about ffdc.
511 FFDCData pelAdditionalData;
512
513 // Adding collected phal logs into PEL additional data
514 for_each(
515 traceLog.begin(), traceLog.end(),
516 [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void {
517 pelAdditionalData.emplace_back(ele.first, ele.second);
518 });
519
520 // reset the trace log and counter
521 reset();
522
523 // get primary processor to collect FFDC/Dump information.
524 struct pdbg_target* procTarget;
525 pdbg_for_each_class_target("proc", procTarget)
526 {
527 if (openpower::phal::isPrimaryProc(procTarget))
528 break;
529 procTarget = nullptr;
530 }
531 // check valid primary processor is available
532 if (procTarget == nullptr)
533 {
534 log<level::ERR>("processSbeBootError: fail to get primary processor");
535 // Initialise the SRC6 with default data, not used in this use case.
536 pelAdditionalData.emplace_back("SRC6", "00000000");
537 openpower::pel::createPEL(
538 "org.open_power.Processor.Error.SbeBootFailure", pelAdditionalData);
539 return;
540 }
541 // SBE error object.
542 sbeError_t sbeError;
543 bool dumpIsRequired = false;
544
545 try
546 {
547 // Capture FFDC information on primary processor
548 sbeError = captureFFDC(procTarget);
549 }
550 catch (const std::exception& e)
551 {
552 // Fail to collect FFDC information , trigger Dump
553 log<level::ERR>(
554 fmt::format("captureFFDC: Exception{}", e.what()).c_str());
555 dumpIsRequired = true;
556 }
557
558 if ((sbeError.errType() == SBE_FFDC_NO_DATA) ||
559 (sbeError.errType() == SBE_CMD_TIMEOUT) || (dumpIsRequired))
560 {
561 // Create SBE Dump type error log and trigger Dump
562 openpower::pel::createPEL(
563 "org.open_power.Processor.Error.SbeBootTimeout", pelAdditionalData);
564 // TODO Add dump request
565 return;
566 }
567 // SRC6 : [0:15] chip position
568 uint32_t word6 = pdbg_target_index(procTarget);
569 pelAdditionalData.emplace_back("SRC6", std::to_string(word6 << 16));
570 // Create SBE Error with FFDC data.
571 createSbeErrorPEL("org.open_power.Processor.Error.SbeBootFailure", sbeError,
572 pelAdditionalData);
573}
574
Brad Bishopf6783cd2020-10-27 19:25:09 -0400575void reset()
576{
577 // reset the trace log and counter
578 traceLog.clear();
579 counter = 0;
580}
581
Brad Bishop63508a72020-10-27 18:55:01 -0400582void pDBGLogTraceCallbackHelper(int, const char* fmt, va_list ap)
Brad Bishopf6783cd2020-10-27 19:25:09 -0400583{
584 processLogTraceCallback(NULL, fmt, ap);
585}
586} // namespace detail
587
588static inline uint8_t getLogLevelFromEnv(const char* env, const uint8_t dValue)
589{
590 auto logLevel = dValue;
591 try
592 {
593 if (const char* env_p = std::getenv(env))
594 {
595 logLevel = std::stoi(env_p);
596 }
597 }
Patrick Williams1a9a5a62021-10-06 13:05:06 -0500598 catch (const std::exception& e)
Brad Bishopf6783cd2020-10-27 19:25:09 -0400599 {
600 log<level::ERR>(("Conversion Failure"), entry("ENVIRONMENT=%s", env),
601 entry("EXCEPTION=%s", e.what()));
602 }
603 return logLevel;
604}
605
606void addBootErrorCallbacks()
607{
608 // Get individual phal repos log level from environment variable
609 // and update the log level.
610 pdbg_set_loglevel(getLogLevelFromEnv("PDBG_LOG", PDBG_INFO));
611 libekb_set_loglevel(getLogLevelFromEnv("LIBEKB_LOG", LIBEKB_LOG_IMP));
612 ipl_set_loglevel(getLogLevelFromEnv("IPL_LOG", IPL_INFO));
613
614 // add callback for debug traces
615 pdbg_set_logfunc(detail::pDBGLogTraceCallbackHelper);
616 libekb_set_logfunc(detail::processLogTraceCallback, NULL);
617 ipl_set_logfunc(detail::processLogTraceCallback, NULL);
618
619 // add callback for ipl failures
Jayanth Othayoth2b211702021-09-06 05:14:23 -0500620 ipl_set_error_callback_func(detail::processIplErrorCallback);
Brad Bishopf6783cd2020-10-27 19:25:09 -0400621}
622
623} // namespace pel
624} // namespace openpower