1 extern "C"
2 {
3 #include <libpdbg.h>
4 }
5
6 #include "create_pel.hpp"
7 #include "dump_utils.hpp"
8 #include "extensions/phal/common_utils.hpp"
9 #include "phal_error.hpp"
10 #include "util.hpp"
11
12 #include <attributes_info.H>
13 #include <libekb.H>
14 #include <libphal.H>
15
16 #include <nlohmann/json.hpp>
17 #include <phosphor-logging/elog.hpp>
18
19 #include <algorithm>
20 #include <cstdlib>
21 #include <cstring>
22 #include <format>
23 #include <iomanip>
24 #include <list>
25 #include <map>
26 #include <sstream>
27 #include <string>
28
29 namespace openpower
30 {
31 namespace phal
32 {
33 using namespace phosphor::logging;
34 using namespace openpower::phal::exception;
35 using Severity = sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level;
36
37 /**
38 * Used to pass buffer to pdbg callback api to get required target
39 * data (attributes) based on given data (attribute).
40 */
41 struct TargetInfo
42 {
43 ATTR_PHYS_BIN_PATH_Type physBinPath;
44 ATTR_LOCATION_CODE_Type locationCode;
45 ATTR_PHYS_DEV_PATH_Type physDevPath;
46 ATTR_MRU_ID_Type mruId;
47
48 bool deconfigure;
49
TargetInfoopenpower::phal::TargetInfo50 TargetInfo()
51 {
52 memset(&physBinPath, '\0', sizeof(physBinPath));
53 memset(&locationCode, '\0', sizeof(locationCode));
54 memset(&physDevPath, '\0', sizeof(physDevPath));
55 mruId = 0;
56 deconfigure = false;
57 }
58 };
59
60 /**
61 * Used to return in callback function which are used to get
62 * physical path value and it binary format value.
63 *
64 * The value for constexpr defined based on pdbg_target_traverse function usage.
65 */
66 constexpr int continueTgtTraversal = 0;
67 constexpr int requireAttrFound = 1;
68 constexpr int requireAttrNotFound = 2;
69
70 /**
71 * @brief Used to get target location code from phal device tree
72 *
73 * @param[in] target current device tree target
74 * @param[out] appPrivData used for accessing|storing from|to application
75 *
76 * @return 0 to continue traverse, non-zero to stop traverse
77 */
pdbgCallbackToGetTgtReqAttrsVal(struct pdbg_target * target,void * appPrivData)78 int pdbgCallbackToGetTgtReqAttrsVal(struct pdbg_target* target,
79 void* appPrivData)
80 {
81 using namespace openpower::phal::pdbg;
82
83 TargetInfo* targetInfo = static_cast<TargetInfo*>(appPrivData);
84
85 ATTR_PHYS_BIN_PATH_Type physBinPath;
86 /**
87 * TODO: Issue: phal/pdata#16
88 * Should not use direct pdbg api to read attribute. Need to use DT_GET_PROP
89 * macro for bmc app's and this will call libdt-api api but, it will print
90 * "pdbg_target_get_attribute failed" trace if attribute is not found and
91 * this callback will call recursively by using pdbg_target_traverse() until
92 * find expected attribute based on return code from this callback. Because,
93 * need to do target iteration to get actual attribute (ATTR_PHYS_BIN_PATH)
94 * value when device tree target info doesn't know to read attribute from
95 * device tree. So, Due to this error trace user will get confusion while
96 * looking traces. Hence using pdbg api to avoid trace until libdt-api
97 * provides log level setup.
98 */
99 if (!pdbg_target_get_attribute(
100 target, "ATTR_PHYS_BIN_PATH",
101 std::stoi(dtAttr::fapi2::ATTR_PHYS_BIN_PATH_Spec),
102 dtAttr::fapi2::ATTR_PHYS_BIN_PATH_ElementCount, physBinPath))
103 {
104 return continueTgtTraversal;
105 }
106
107 if (std::memcmp(physBinPath, targetInfo->physBinPath,
108 sizeof(physBinPath)) != 0)
109 {
110 return continueTgtTraversal;
111 }
112
113 // Found Target, now collect the required attributes associated to the
114 // target. Incase of any attribute read failure, initialize the data with
115 // default value.
116 try
117 {
118 // Get location code information
119 openpower::phal::pdbg::getLocationCode(target,
120 targetInfo->locationCode);
121 }
122 catch (const std::exception& e)
123 {
124 log<level::ERR>(std::format("getLocationCode({}): Exception({})",
125 pdbg_target_path(target), e.what())
126 .c_str());
127 }
128
129 if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, target, targetInfo->physDevPath))
130 {
131 log<level::ERR>(
132 std::format("Could not read({}) PHYS_DEV_PATH attribute",
133 pdbg_target_path(target))
134 .c_str());
135 }
136
137 if (DT_GET_PROP(ATTR_MRU_ID, target, targetInfo->mruId))
138 {
139 log<level::ERR>(std::format("Could not read({}) ATTR_MRU_ID attribute",
140 pdbg_target_path(target))
141 .c_str());
142 }
143
144 return requireAttrFound;
145 }
146
147 /**
148 * @brief Used to get target info (attributes data)
149 *
150 * To get target required attributes value using another attribute value
151 * ("PHYS_BIN_PATH" which is present in same target attributes list) by using
152 * "ipdbg_target_traverse" api because, here we have attribute value only and
153 * doesn't have respective device tree target info to get required attributes
154 * values from it attributes list.
155 *
156 * @param[in] physBinPath to pass PHYS_BIN_PATH value
157 * @param[out] targetInfo to pas buufer to fill with required attributes
158 *
159 * @return true on success otherwise false
160 */
getTgtReqAttrsVal(const std::vector<uint8_t> & physBinPath,TargetInfo & targetInfo)161 bool getTgtReqAttrsVal(const std::vector<uint8_t>& physBinPath,
162 TargetInfo& targetInfo)
163 {
164 std::memcpy(&targetInfo.physBinPath, physBinPath.data(),
165 sizeof(targetInfo.physBinPath));
166
167 int ret = pdbg_target_traverse(NULL, pdbgCallbackToGetTgtReqAttrsVal,
168 &targetInfo);
169 if (ret == 0)
170 {
171 std::string fmt;
172 for (auto value : targetInfo.physBinPath)
173 {
174 fmt += std::format("{:02X} ", value);
175 }
176
177 log<level::ERR>(std::format("Given ATTR_PHYS_BIN_PATH value {} "
178 "not found in phal device tree",
179 fmt)
180 .c_str());
181 return false;
182 }
183 else if (ret == requireAttrNotFound)
184 {
185 return false;
186 }
187
188 return true;
189 }
190 } // namespace phal
191
192 namespace pel
193 {
194 using namespace phosphor::logging;
195
196 namespace detail
197 {
198 using json = nlohmann::json;
199
200 // keys need to be unique so using counter value to generate unique key
201 static int counter = 0;
202
203 // list of debug traces
204 static std::vector<std::pair<std::string, std::string>> traceLog;
205
206 /**
207 * @brief Process platform realted boot failure
208 *
209 * @param[in] errInfo - error details
210 */
211 static void processPlatBootError(const ipl_error_info& errInfo);
212
processLogTraceCallback(void *,const char * fmt,va_list ap)213 void processLogTraceCallback(void*, const char* fmt, va_list ap)
214 {
215 va_list vap;
216 va_copy(vap, ap);
217 std::vector<char> logData(1 + std::vsnprintf(nullptr, 0, fmt, ap));
218 std::vsnprintf(logData.data(), logData.size(), fmt, vap);
219 va_end(vap);
220 std::string logstr(logData.begin(), logData.end());
221
222 log<level::INFO>(logstr.c_str());
223
224 char timeBuf[80];
225 time_t t = time(0);
226 tm myTm{};
227 gmtime_r(&t, &myTm);
228 strftime(timeBuf, 80, "%Y-%m-%d %H:%M:%S", &myTm);
229
230 // key values need to be unique for PEL
231 // TODO #openbmc/dev/issues/1563
232 // If written to Json no need to worry about unique KEY
233 std::stringstream str;
234 str << std::setfill('0');
235 str << "LOG" << std::setw(3) << counter;
236 str << " " << timeBuf;
237 traceLog.emplace_back(std::make_pair(str.str(), std::move(logstr)));
238 counter++;
239 }
240
241 /**
242 * @brief GET PEL priority from pHAL priority
243 *
244 * The pHAL callout priority is in different format than PEL format
245 * so, this api is used to return current phal supported priority into
246 * PEL expected format.
247 *
248 * @param[in] phalPriority used to pass phal priority format string
249 *
250 * @return pel priority format string else empty if failure
251 *
252 * @note For "NONE" returning "L" (LOW)
253 */
getPelPriority(const std::string & phalPriority)254 static std::string getPelPriority(const std::string& phalPriority)
255 {
256 const std::map<std::string, std::string> priorityMap = {
257 {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}};
258
259 auto it = priorityMap.find(phalPriority);
260 if (it == priorityMap.end())
261 {
262 log<level::ERR>(std::format("Unsupported phal priority({}) is given "
263 "to get pel priority format",
264 phalPriority)
265 .c_str());
266 return "H";
267 }
268
269 return it->second;
270 }
271
272 /**
273 * @brief Helper function to create PEL for non functional boot
274 * processor related failure.
275 * This function adds the BMC code callout as priority 1 to fix
276 * devtree related software issue. Incase the issue still persist
277 * after reboot recommend to replacing the primary processor.
278 */
processNonFunctionalBootProc()279 void processNonFunctionalBootProc()
280 {
281 json jsonCalloutDataList;
282 json jsonProcedCallout;
283 // Add BMC code callout
284 jsonProcedCallout["Procedure"] = "BMC0001";
285 jsonProcedCallout["Priority"] = "H";
286 jsonCalloutDataList.emplace_back(std::move(jsonProcedCallout));
287
288 // get primary processor
289 struct pdbg_target* procTarget;
290 pdbg_for_each_class_target("proc", procTarget)
291 {
292 if (openpower::phal::isPrimaryProc(procTarget))
293 break;
294 procTarget = nullptr;
295 }
296 // check valid primary processor is available
297 if (procTarget == nullptr)
298 {
299 log<level::ERR>(
300 "processNonFunctionalBootProc: fail to get primary processor");
301 }
302 else
303 {
304 try
305 {
306 ATTR_LOCATION_CODE_Type locationCode = {'\0'};
307 // Get location code information
308 openpower::phal::pdbg::getLocationCode(procTarget, locationCode);
309 json jsonProcCallout;
310 jsonProcCallout["LocationCode"] = locationCode;
311 jsonProcCallout["Deconfigured"] = false;
312 jsonProcCallout["Guarded"] = false;
313 jsonProcCallout["Priority"] = "M";
314 jsonCalloutDataList.emplace_back(std::move(jsonProcCallout));
315 }
316 catch (const std::exception& e)
317 {
318 log<level::ERR>(std::format("getLocationCode({}): Exception({})",
319 pdbg_target_path(procTarget), e.what())
320 .c_str());
321 }
322 }
323 // Adding collected phal logs into PEL additional data
324 FFDCData pelAdditionalData;
325 for_each(
326 traceLog.begin(), traceLog.end(),
327 [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void {
328 pelAdditionalData.emplace_back(ele.first, ele.second);
329 });
330 openpower::pel::createErrorPEL(
331 "org.open_power.PHAL.Error.NonFunctionalBootProc", jsonCalloutDataList,
332 pelAdditionalData, Severity::Error);
333 // reset trace log and exit
334 reset();
335 }
336
337 /**
338 * @brief processClockInfoErrorHelper
339 *
340 * Creates informational PEL for spare clock failure
341 *
342 * @param[in] ffdc - FFDC data capturd by the HWP
343 * @param[in] ffdc_prefix - prefix string for logging the data.
344 */
processClockInfoErrorHelper(FFDC * ffdc,const std::string & ffdc_prefix)345 void processClockInfoErrorHelper(FFDC* ffdc, const std::string& ffdc_prefix)
346 {
347 try
348 {
349 log<level::INFO>(
350 std::format("processClockInfoErrorHelper: FFDC Message[{}]",
351 ffdc->message)
352 .c_str());
353
354 // To store callouts details in json format as per pel expectation.
355 json jsonCalloutDataList;
356 jsonCalloutDataList = json::array();
357
358 // To store phal trace and other additional data about ffdc.
359 FFDCData pelAdditionalData;
360
361 std::string keyWithPrefix(ffdc_prefix + "RC");
362 // Adding hardware procedures return code details
363 pelAdditionalData.emplace_back(keyWithPrefix, ffdc->hwp_errorinfo.rc);
364 keyWithPrefix = ffdc_prefix + "RC_DESC";
365 pelAdditionalData.emplace_back(keyWithPrefix,
366 ffdc->hwp_errorinfo.rc_desc);
367
368 // Adding hardware procedures required ffdc data for debug
369 for_each(ffdc->hwp_errorinfo.ffdcs_data.begin(),
370 ffdc->hwp_errorinfo.ffdcs_data.end(),
371 [&pelAdditionalData, &ffdc_prefix](
372 std::pair<std::string, std::string>& ele) -> void {
373 std::string keyWithPrefix(ffdc_prefix + "FFDC_");
374 keyWithPrefix.append(ele.first);
375
376 pelAdditionalData.emplace_back(keyWithPrefix, ele.second);
377 });
378 // get clock position information
379 auto clk_pos = 0xFF; // Invalid position.
380 for (auto& hwCallout : ffdc->hwp_errorinfo.hwcallouts)
381 {
382 if ((hwCallout.hwid == "PROC_REF_CLOCK") ||
383 (hwCallout.hwid == "PCI_REF_CLOCK"))
384 {
385 clk_pos = hwCallout.clkPos;
386 break;
387 }
388 }
389
390 // Adding CDG (Only deconfigure) targets details
391 for_each(ffdc->hwp_errorinfo.cdg_targets.begin(),
392 ffdc->hwp_errorinfo.cdg_targets.end(),
393 [&pelAdditionalData, &jsonCalloutDataList,
394 clk_pos](const CDG_Target& cdg_tgt) -> void {
395 json jsonCalloutData;
396 std::string pelPriority = "L";
397 jsonCalloutData["Priority"] = pelPriority; // Not used
398 jsonCalloutData["SymbolicFRU"] = "REFCLK" + std::to_string(clk_pos);
399 jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
400 jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path;
401 jsonCalloutDataList.emplace_back(jsonCalloutData);
402 });
403
404 // Adding collected phal logs into PEL additional data
405 for_each(traceLog.begin(), traceLog.end(),
406 [&pelAdditionalData](
407 std::pair<std::string, std::string>& ele) -> void {
408 pelAdditionalData.emplace_back(ele.first, ele.second);
409 });
410
411 openpower::pel::createErrorPEL("org.open_power.PHAL.Error.SpareClock",
412 jsonCalloutDataList, pelAdditionalData,
413 Severity::Informational);
414 }
415 catch (const std::exception& ex)
416 {
417 reset();
418 throw ex;
419 }
420 reset();
421 }
422
processIplErrorCallback(const ipl_error_info & errInfo)423 void processIplErrorCallback(const ipl_error_info& errInfo)
424 {
425 log<level::INFO>(
426 std::format("processIplErrorCallback: Error type({})",
427 static_cast<std::underlying_type<ipl_error_type>::type>(
428 errInfo.type))
429 .c_str());
430
431 switch (errInfo.type)
432 {
433 case IPL_ERR_OK:
434 // reset trace log and exit
435 reset();
436 break;
437 case IPL_ERR_SBE_BOOT:
438 case IPL_ERR_SBE_CHIPOP:
439 // handle SBE related failures.
440 processSbeBootError();
441 break;
442 case IPL_ERR_HWP:
443 // Handle hwp failure
444 processBootError(false);
445 break;
446 case IPL_ERR_PLAT:
447 processPlatBootError(errInfo);
448 break;
449 case IPL_ERR_PRI_PROC_NON_FUNC:
450 // Handle non functional boot processor error.
451 processNonFunctionalBootProc();
452 break;
453 case IPL_ERR_GUARD_PARTITION_ACCESS:
454 processGuardPartitionAccessError();
455 break;
456 default:
457 createPEL("org.open_power.PHAL.Error.Boot");
458 // reset trace log and exit
459 reset();
460 break;
461 }
462 }
463
464 /**
465 * @brief addPlanarCallout
466 *
467 * This function will add a json for planar callout in the input json list.
468 * The caller can pass this json list into createErrorPEL to apply the callout.
469 *
470 * @param[in,out] jsonCalloutDataList - json list where callout json will be
471 * emplaced
472 * @param[in] priority - string indicating priority.
473 */
addPlanarCallout(json & jsonCalloutDataList,const std::string & priority)474 static void addPlanarCallout(json& jsonCalloutDataList,
475 const std::string& priority)
476 {
477 json jsonCalloutData;
478
479 // Inventory path for planar
480 jsonCalloutData["InventoryPath"] =
481 "/xyz/openbmc_project/inventory/system/chassis/motherboard";
482 jsonCalloutData["Deconfigured"] = false;
483 jsonCalloutData["Guarded"] = false;
484 jsonCalloutData["Priority"] = priority;
485
486 jsonCalloutDataList.emplace_back(jsonCalloutData);
487 }
488
489 /**
490 * @brief processPoweroffError
491 *
492 * Creates informational PEL for the PLAT/HWP error occured during poweroff
493 *
494 * Not adding callouts from FFDC as the hardware errors in the poweroff path
495 * should be non-visible. so that we don't throw out extraneous callouts for
496 * power errors or because the CEC is just not in an expected state.
497 *
498 * @param[in] ffdc - FFDC data capturd by the HWP
499 * @param[in] ffdc_prefix - prefix string for logging the data.
500 */
501
processPoweroffError(FFDC * ffdc,const std::string & ffdc_prefix)502 void processPoweroffError(FFDC* ffdc, const std::string& ffdc_prefix)
503 {
504 try
505 {
506 log<level::INFO>(
507 std::format("processPoweroffError: Message[{}]", ffdc->message)
508 .c_str());
509
510 // To store phal trace and other additional data about ffdc.
511 FFDCData pelAdditionalData;
512
513 if (ffdc->ffdc_type == FFDC_TYPE_HWP)
514 {
515 std::string keyWithPrefix(ffdc_prefix + "RC");
516 // Adding hardware procedures return code details
517 pelAdditionalData.emplace_back(keyWithPrefix,
518 ffdc->hwp_errorinfo.rc);
519 keyWithPrefix = ffdc_prefix + "RC_DESC";
520 pelAdditionalData.emplace_back(keyWithPrefix,
521 ffdc->hwp_errorinfo.rc_desc);
522 }
523 else if ((ffdc->ffdc_type != FFDC_TYPE_NONE) &&
524 (ffdc->ffdc_type != FFDC_TYPE_UNSUPPORTED))
525 {
526 log<level::ERR>(
527 std::format("Unsupported phal FFDC type to create PEL. "
528 "MSG: {}",
529 ffdc->message)
530 .c_str());
531 }
532
533 // Adding collected phal logs into PEL additional data
534 for_each(traceLog.begin(), traceLog.end(),
535 [&pelAdditionalData](
536 std::pair<std::string, std::string>& ele) -> void {
537 pelAdditionalData.emplace_back(ele.first, ele.second);
538 });
539
540 openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot", {},
541 pelAdditionalData,
542 Severity::Informational);
543 }
544 catch (const std::exception& ex)
545 {
546 reset();
547 throw ex;
548 }
549 reset();
550 }
551
processBootErrorHelper(FFDC * ffdc,const std::string & ffdc_prefix)552 void processBootErrorHelper(FFDC* ffdc, const std::string& ffdc_prefix)
553 {
554 log<level::INFO>("processBootErrorHelper ");
555 try
556 {
557 log<level::INFO>(
558 std::format("PHAL FFDC: Return Message[{}]", ffdc->message)
559 .c_str());
560
561 // Special handling for spare clock related errors.
562 if (ffdc->ffdc_type == FFDC_TYPE_SPARE_CLOCK_INFO)
563 {
564 processClockInfoErrorHelper(ffdc, ffdc_prefix);
565 return;
566 }
567 // To store callouts details in json format as per pel expectation.
568 json jsonCalloutDataList;
569 jsonCalloutDataList = json::array();
570
571 // To store phal trace and other additional data about ffdc.
572 FFDCData pelAdditionalData;
573
574 if (ffdc->ffdc_type == FFDC_TYPE_HWP)
575 {
576 std::string keyWithPrefix(ffdc_prefix + "RC");
577 // Adding hardware procedures return code details
578 pelAdditionalData.emplace_back(keyWithPrefix,
579 ffdc->hwp_errorinfo.rc);
580 keyWithPrefix = ffdc_prefix + "RC_DESC";
581 pelAdditionalData.emplace_back(keyWithPrefix,
582 ffdc->hwp_errorinfo.rc_desc);
583
584 // Adding hardware procedures required ffdc data for debug
585 for_each(ffdc->hwp_errorinfo.ffdcs_data.begin(),
586 ffdc->hwp_errorinfo.ffdcs_data.end(),
587 [&pelAdditionalData, &ffdc_prefix](
588 std::pair<std::string, std::string>& ele) -> void {
589 std::string keyWithPrefix(ffdc_prefix + "FFDC_");
590 keyWithPrefix.append(ele.first);
591
592 pelAdditionalData.emplace_back(keyWithPrefix, ele.second);
593 });
594
595 // Adding hardware callout details
596 int calloutCount = 0;
597 for_each(ffdc->hwp_errorinfo.hwcallouts.begin(),
598 ffdc->hwp_errorinfo.hwcallouts.end(),
599 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList,
600 &ffdc_prefix](const HWCallout& hwCallout) -> void {
601 calloutCount++;
602 std::stringstream keyPrefix;
603 keyPrefix << ffdc_prefix << "HW_CO_" << std::setfill('0')
604 << std::setw(2) << calloutCount << "_";
605
606 pelAdditionalData.emplace_back(
607 std::string(keyPrefix.str()).append("HW_ID"),
608 hwCallout.hwid);
609
610 pelAdditionalData.emplace_back(
611 std::string(keyPrefix.str()).append("PRIORITY"),
612 hwCallout.callout_priority);
613
614 // Log target details only if entity path is
615 // available. For example target entity path will not
616 // be available for non-hwp clock failure.
617 if (!hwCallout.target_entity_path.empty())
618 {
619 phal::TargetInfo targetInfo;
620 phal::getTgtReqAttrsVal(hwCallout.target_entity_path,
621 targetInfo);
622
623 std::string locationCode =
624 std::string(targetInfo.locationCode);
625 pelAdditionalData.emplace_back(
626 std::string(keyPrefix.str()).append("LOC_CODE"),
627 locationCode);
628
629 std::string physPath = std::string(targetInfo.physDevPath);
630 pelAdditionalData.emplace_back(
631 std::string(keyPrefix.str()).append("PHYS_PATH"),
632 physPath);
633 }
634
635 pelAdditionalData.emplace_back(
636 std::string(keyPrefix.str()).append("CLK_POS"),
637 std::to_string(hwCallout.clkPos));
638
639 pelAdditionalData.emplace_back(
640 std::string(keyPrefix.str()).append("CALLOUT_PLANAR"),
641 (hwCallout.isPlanarCallout == true ? "true" : "false"));
642
643 std::string pelPriority =
644 getPelPriority(hwCallout.callout_priority);
645
646 if (hwCallout.isPlanarCallout)
647 {
648 addPlanarCallout(jsonCalloutDataList, pelPriority);
649 }
650 });
651
652 // Adding CDG (callout, deconfigure and guard) targets details
653 calloutCount = 0;
654 for_each(ffdc->hwp_errorinfo.cdg_targets.begin(),
655 ffdc->hwp_errorinfo.cdg_targets.end(),
656 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList,
657 &ffdc_prefix](const CDG_Target& cdg_tgt) -> void {
658 calloutCount++;
659 std::stringstream keyPrefix;
660 keyPrefix << ffdc_prefix << "CDG_TGT_" << std::setfill('0')
661 << std::setw(2) << calloutCount << "_";
662
663 phal::TargetInfo targetInfo;
664 targetInfo.deconfigure = cdg_tgt.deconfigure;
665
666 phal::getTgtReqAttrsVal(cdg_tgt.target_entity_path, targetInfo);
667
668 std::string locationCode = std::string(targetInfo.locationCode);
669 pelAdditionalData.emplace_back(
670 std::string(keyPrefix.str()).append("LOC_CODE"),
671 locationCode);
672 std::string physPath = std::string(targetInfo.physDevPath);
673 pelAdditionalData.emplace_back(
674 std::string(keyPrefix.str()).append("PHYS_PATH"), physPath);
675
676 pelAdditionalData.emplace_back(
677 std::string(keyPrefix.str()).append("CO_REQ"),
678 (cdg_tgt.callout == true ? "true" : "false"));
679
680 pelAdditionalData.emplace_back(
681 std::string(keyPrefix.str()).append("CO_PRIORITY"),
682 cdg_tgt.callout_priority);
683
684 pelAdditionalData.emplace_back(
685 std::string(keyPrefix.str()).append("DECONF_REQ"),
686 (cdg_tgt.deconfigure == true ? "true" : "false"));
687
688 pelAdditionalData.emplace_back(
689 std::string(keyPrefix.str()).append("GUARD_REQ"),
690 (cdg_tgt.guard == true ? "true" : "false"));
691
692 pelAdditionalData.emplace_back(
693 std::string(keyPrefix.str()).append("GUARD_TYPE"),
694 cdg_tgt.guard_type);
695
696 json jsonCalloutData;
697 jsonCalloutData["LocationCode"] = locationCode;
698 std::string pelPriority =
699 getPelPriority(cdg_tgt.callout_priority);
700 jsonCalloutData["Priority"] = pelPriority;
701
702 if (targetInfo.mruId != 0)
703 {
704 jsonCalloutData["MRUs"] = json::array({
705 {{"ID", targetInfo.mruId}, {"Priority", pelPriority}},
706 });
707 }
708 jsonCalloutData["Deconfigured"] = cdg_tgt.deconfigure;
709 jsonCalloutData["Guarded"] = cdg_tgt.guard;
710 jsonCalloutData["GuardType"] = cdg_tgt.guard_type;
711 jsonCalloutData["EntityPath"] = cdg_tgt.target_entity_path;
712
713 jsonCalloutDataList.emplace_back(jsonCalloutData);
714 });
715 // Adding procedure callout
716 calloutCount = 0;
717 for_each(
718 ffdc->hwp_errorinfo.procedures_callout.begin(),
719 ffdc->hwp_errorinfo.procedures_callout.end(),
720 [&pelAdditionalData, &calloutCount, &jsonCalloutDataList,
721 &ffdc_prefix](const ProcedureCallout& procCallout) -> void {
722 calloutCount++;
723 std::stringstream keyPrefix;
724 keyPrefix << ffdc_prefix << "PROC_CO_" << std::setfill('0')
725 << std::setw(2) << calloutCount << "_";
726
727 pelAdditionalData.emplace_back(
728 std::string(keyPrefix.str()).append("PRIORITY"),
729 procCallout.callout_priority);
730
731 pelAdditionalData.emplace_back(
732 std::string(keyPrefix.str()).append("MAINT_PROCEDURE"),
733 procCallout.proc_callout);
734
735 json jsonCalloutData;
736 jsonCalloutData["Procedure"] = procCallout.proc_callout;
737 std::string pelPriority =
738 getPelPriority(procCallout.callout_priority);
739 jsonCalloutData["Priority"] = pelPriority;
740 jsonCalloutDataList.emplace_back(jsonCalloutData);
741 });
742 }
743 else if ((ffdc->ffdc_type != FFDC_TYPE_NONE) &&
744 (ffdc->ffdc_type != FFDC_TYPE_UNSUPPORTED))
745 {
746 log<level::ERR>(
747 std::format("Unsupported phal FFDC type to create PEL. "
748 "MSG: {}",
749 ffdc->message)
750 .c_str());
751 }
752
753 // Adding collected phal logs into PEL additional data
754 for_each(traceLog.begin(), traceLog.end(),
755 [&pelAdditionalData](
756 std::pair<std::string, std::string>& ele) -> void {
757 pelAdditionalData.emplace_back(ele.first, ele.second);
758 });
759
760 // TODO: #ibm-openbmc/dev/issues/2595 : Once enabled this support,
761 // callout details is not required to sort in H,M and L orders which
762 // are expected by pel because, pel will take care for sorting callouts
763 // based on priority so, now adding support to send callout in order
764 // i.e High -> Medium -> Low.
765 std::sort(jsonCalloutDataList.begin(), jsonCalloutDataList.end(),
766 [](const json& aEle, const json& bEle) -> bool {
767 // Considering b element having higher priority than a element
768 // or Both element will be same priorty (to keep same order
769 // which are given by phal when two callouts are having same
770 // priority)
771 if (((aEle["Priority"] == "M") && (bEle["Priority"] == "H")) ||
772 ((aEle["Priority"] == "L") &&
773 ((bEle["Priority"] == "H") || (bEle["Priority"] == "M"))) ||
774 (aEle["Priority"] == bEle["Priority"]))
775 {
776 return false;
777 }
778
779 // Considering a element having higher priority than b element
780 return true;
781 });
782 openpower::pel::createErrorPEL("org.open_power.PHAL.Error.Boot",
783 jsonCalloutDataList, pelAdditionalData,
784 Severity::Error);
785 }
786 catch (const std::exception& ex)
787 {
788 reset();
789 throw ex;
790 }
791 reset();
792 }
793
processPlatBootError(const ipl_error_info & errInfo)794 void processPlatBootError(const ipl_error_info& errInfo)
795 {
796 log<level::INFO>("processPlatBootError ");
797
798 // Collecting ffdc details from phal
799 FFDC* ffdc = reinterpret_cast<FFDC*>(errInfo.private_data);
800 try
801 {
802 if (util::isHostPoweringOff())
803 {
804 processPoweroffError(ffdc, "PLAT_");
805 }
806 else
807 {
808 processBootErrorHelper(ffdc, "PLAT_");
809 }
810 }
811 catch (const std::exception& ex)
812 {
813 log<level::ERR>(
814 std::format("processPlatBootError: exception({})", ex.what())
815 .c_str());
816 throw ex;
817 }
818 }
819
processBootError(bool status)820 void processBootError(bool status)
821 {
822 log<level::INFO>(
823 std::format("processBootError: status({})", status).c_str());
824
825 try
826 {
827 // return If no failure during hwp execution
828 if (status)
829 return;
830 // Collecting ffdc details from phal
831 FFDC ffdc;
832 libekb_get_ffdc(ffdc);
833
834 if (util::isHostPoweringOff())
835 {
836 processPoweroffError(&ffdc, "HWP_");
837 }
838 else
839 {
840 processBootErrorHelper(&ffdc, "HWP_");
841 }
842 }
843 catch (const std::exception& ex)
844 {
845 log<level::ERR>(
846 std::format("processBootError: exception({})", ex.what()).c_str());
847 throw ex;
848 }
849 }
850
processSbeBootError()851 void processSbeBootError()
852 {
853 log<level::INFO>("processSbeBootError : Entered ");
854
855 using namespace openpower::phal::sbe;
856
857 // To store phal trace and other additional data about ffdc.
858 FFDCData pelAdditionalData;
859
860 // Adding collected phal logs into PEL additional data
861 for_each(
862 traceLog.begin(), traceLog.end(),
863 [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void {
864 pelAdditionalData.emplace_back(ele.first, ele.second);
865 });
866
867 // reset the trace log and counter
868 reset();
869
870 // get primary processor to collect FFDC/Dump information.
871 struct pdbg_target* procTarget;
872 pdbg_for_each_class_target("proc", procTarget)
873 {
874 if (openpower::phal::isPrimaryProc(procTarget))
875 break;
876 procTarget = nullptr;
877 }
878 // check valid primary processor is available
879 if (procTarget == nullptr)
880 {
881 log<level::ERR>("processSbeBootError: fail to get primary processor");
882 // Add BMC code callout and create PEL
883 json jsonCalloutDataList;
884 jsonCalloutDataList = json::array();
885 json jsonCalloutData;
886 jsonCalloutData["Procedure"] = "BMC0001";
887 jsonCalloutData["Priority"] = "H";
888 jsonCalloutDataList.emplace_back(jsonCalloutData);
889 openpower::pel::createErrorPEL(
890 "org.open_power.Processor.Error.SbeBootFailure",
891 jsonCalloutDataList, {}, Severity::Error);
892 return;
893 }
894 // SBE error object.
895 sbeError_t sbeError;
896 bool dumpIsRequired = false;
897
898 try
899 {
900 // Capture FFDC information on primary processor
901 sbeError = captureFFDC(procTarget);
902 }
903 catch (const phalError_t& phalError)
904 {
905 // Fail to collect FFDC information , trigger Dump
906 log<level::ERR>(
907 std::format("captureFFDC: Exception({})", phalError.what())
908 .c_str());
909 dumpIsRequired = true;
910 }
911
912 std::string event;
913
914 if ((sbeError.errType() == SBE_FFDC_NO_DATA) ||
915 (sbeError.errType() == SBE_CMD_TIMEOUT) || (dumpIsRequired))
916 {
917 event = "org.open_power.Processor.Error.SbeBootTimeout";
918 dumpIsRequired = true;
919 }
920 else
921 {
922 event = "org.open_power.Processor.Error.SbeBootFailure";
923 }
924 // SRC6 : [0:15] chip position
925 uint32_t index = pdbg_target_index(procTarget);
926 pelAdditionalData.emplace_back("SRC6", std::to_string(index << 16));
927 // Create SBE Error with FFDC data.
928 auto logId = createSbeErrorPEL(event, sbeError, pelAdditionalData,
929 procTarget);
930
931 if (dumpIsRequired)
932 {
933 using namespace openpower::phal::dump;
934 DumpParameters dumpParameters = {logId, index, SBE_DUMP_TIMEOUT,
935 DumpType::SBE};
936 try
937 {
938 requestDump(dumpParameters);
939 }
940 catch (const std::runtime_error& e)
941 {
942 // Allowing call back to handle the error gracefully.
943 log<level::ERR>("Dump collection failed");
944 // TODO revist error handling.
945 }
946 }
947 }
948
processGuardPartitionAccessError()949 void processGuardPartitionAccessError()
950 {
951 // Adding collected phal logs into PEL additional data
952 FFDCData pelAdditionalData;
953
954 for_each(
955 traceLog.begin(), traceLog.end(),
956 [&pelAdditionalData](std::pair<std::string, std::string>& ele) -> void {
957 pelAdditionalData.emplace_back(ele.first, ele.second);
958 });
959
960 openpower::pel::createPEL("org.open_power.PHAL.Error.GuardPartitionAccess",
961 pelAdditionalData);
962 }
963
reset()964 void reset()
965 {
966 // reset the trace log and counter
967 traceLog.clear();
968 counter = 0;
969 }
970
pDBGLogTraceCallbackHelper(int,const char * fmt,va_list ap)971 void pDBGLogTraceCallbackHelper(int, const char* fmt, va_list ap)
972 {
973 processLogTraceCallback(NULL, fmt, ap);
974 }
975 } // namespace detail
976
getLogLevelFromEnv(const char * env,const uint8_t dValue)977 static inline uint8_t getLogLevelFromEnv(const char* env, const uint8_t dValue)
978 {
979 auto logLevel = dValue;
980 try
981 {
982 if (const char* env_p = std::getenv(env))
983 {
984 logLevel = std::stoi(env_p);
985 }
986 }
987 catch (const std::exception& e)
988 {
989 log<level::ERR>(("Conversion Failure"), entry("ENVIRONMENT=%s", env),
990 entry("EXCEPTION=%s", e.what()));
991 }
992 return logLevel;
993 }
994
addBootErrorCallbacks()995 void addBootErrorCallbacks()
996 {
997 // Get individual phal repos log level from environment variable
998 // and update the log level.
999 pdbg_set_loglevel(getLogLevelFromEnv("PDBG_LOG", PDBG_INFO));
1000 libekb_set_loglevel(getLogLevelFromEnv("LIBEKB_LOG", LIBEKB_LOG_IMP));
1001 ipl_set_loglevel(getLogLevelFromEnv("IPL_LOG", IPL_INFO));
1002
1003 // add callback for debug traces
1004 pdbg_set_logfunc(detail::pDBGLogTraceCallbackHelper);
1005 libekb_set_logfunc(detail::processLogTraceCallback, NULL);
1006 ipl_set_logfunc(detail::processLogTraceCallback, NULL);
1007
1008 // add callback for ipl failures
1009 ipl_set_error_callback_func(detail::processIplErrorCallback);
1010 }
1011
1012 } // namespace pel
1013 } // namespace openpower
1014