blob: 9f08f5b4a8cc29db052a8d4ec0855b32438a6c6a [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"
7#include "phal_error.hpp"
8
9#include <attributes_info.H>
10#include <fmt/format.h>
11#include <libekb.H>
12#include <libipl.H>
13
Brad Bishop5e5d4452020-10-27 19:46:13 -040014#include <nlohmann/json.hpp>
15#include <phosphor-logging/elog.hpp>
16
Brad Bishopf6783cd2020-10-27 19:25:09 -040017#include <algorithm>
18#include <cstdlib>
19#include <cstring>
20#include <iomanip>
21#include <list>
22#include <map>
Brad Bishopf6783cd2020-10-27 19:25:09 -040023#include <sstream>
24#include <string>
25
26namespace openpower
27{
28namespace phal
29{
30using namespace phosphor::logging;
31
32/**
33 * Used to pass buffer to pdbg callback api to get required target
34 * data (attributes) based on given data (attribute).
35 */
36struct 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 */
61constexpr int continueTgtTraversal = 0;
62constexpr int requireAttrFound = 1;
63constexpr 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 */
73int pdbgCallbackToGetTgtReqAttrsVal(struct pdbg_target* target,
74 void* appPrivData)
75{
76 TargetInfo* targetInfo = static_cast<TargetInfo*>(appPrivData);
77
78 ATTR_PHYS_BIN_PATH_Type physBinPath;
79 /**
80 * TODO: Issue: phal/pdata#16
81 * Should not use direct pdbg api to read attribute. Need to use DT_GET_PROP
82 * macro for bmc app's and this will call libdt-api api but, it will print
83 * "pdbg_target_get_attribute failed" trace if attribute is not found and
84 * this callback will call recursively by using pdbg_target_traverse() until
85 * find expected attribute based on return code from this callback. Because,
86 * need to do target iteration to get actual attribute (ATTR_PHYS_BIN_PATH)
87 * value when device tree target info doesn't know to read attribute from
88 * device tree. So, Due to this error trace user will get confusion while
89 * looking traces. Hence using pdbg api to avoid trace until libdt-api
90 * provides log level setup.
91 */
92 if (!pdbg_target_get_attribute(
93 target, "ATTR_PHYS_BIN_PATH",
94 std::stoi(dtAttr::fapi2::ATTR_PHYS_BIN_PATH_Spec),
95 dtAttr::fapi2::ATTR_PHYS_BIN_PATH_ElementCount, physBinPath))
96 {
97 return continueTgtTraversal;
98 }
99
100 if (std::memcmp(physBinPath, targetInfo->physBinPath,
101 sizeof(physBinPath)) != 0)
102 {
103 return continueTgtTraversal;
104 }
105
106 if (DT_GET_PROP(ATTR_LOCATION_CODE, target, targetInfo->locationCode))
107 {
108 log<level::ERR>("Could not read LOCATION_CODE attribute");
109 return requireAttrNotFound;
110 }
111
112 if (DT_GET_PROP(ATTR_PHYS_DEV_PATH, target, targetInfo->physDevPath))
113 {
114 log<level::ERR>("Could not read PHYS_DEV_PATH attribute");
115 return requireAttrNotFound;
116 }
117
118 if (DT_GET_PROP(ATTR_MRU_ID, target, targetInfo->mruId))
119 {
120 log<level::ERR>("Could not read MRU_ID attribute");
121 return requireAttrNotFound;
122 }
123
124 if (targetInfo->deconfigure)
125 {
126 ATTR_HWAS_STATE_Type hwasState;
127 if (DT_GET_PROP(ATTR_HWAS_STATE, target, hwasState))
128 {
129 log<level::ERR>("Could not read HWAS_STATE attribute");
130 return requireAttrNotFound;
131 }
132
133 log<level::INFO>(fmt::format("Marking target({}) as Non-Functional",
134 targetInfo->physDevPath)
135 .c_str());
136 hwasState.functional = 0;
137
138 if (DT_SET_PROP(ATTR_HWAS_STATE, target, hwasState))
139 {
140 log<level::ERR>("Could not write HWAS_STATE attribute");
141 return requireAttrNotFound;
142 }
143 }
144
145 return requireAttrFound;
146}
147
148/**
149 * @brief Used to get target info (attributes data)
150 *
151 * To get target required attributes value using another attribute value
152 * ("PHYS_BIN_PATH" which is present in same target attributes list) by using
153 * "ipdbg_target_traverse" api because, here we have attribute value only and
154 * doesn't have respective device tree target info to get required attributes
155 * values from it attributes list.
156 *
157 * @param[in] physBinPath to pass PHYS_BIN_PATH value
158 * @param[out] targetInfo to pas buufer to fill with required attributes
159 *
160 * @return true on success otherwise false
161 */
162bool getTgtReqAttrsVal(const std::vector<uint8_t>& physBinPath,
163 TargetInfo& targetInfo)
164{
165 std::memcpy(&targetInfo.physBinPath, physBinPath.data(),
166 sizeof(targetInfo.physBinPath));
167
168 int ret = pdbg_target_traverse(NULL, pdbgCallbackToGetTgtReqAttrsVal,
169 &targetInfo);
170 if (ret == 0)
171 {
172 log<level::ERR>(fmt::format("Given ATTR_PHYS_BIN_PATH value({}) "
173 "not found in phal device tree",
174 targetInfo.physBinPath)
175 .c_str());
176 return false;
177 }
178 else if (ret == requireAttrNotFound)
179 {
180 return false;
181 }
182
183 return true;
184}
185} // namespace phal
186
187namespace pel
188{
189using namespace phosphor::logging;
190
191namespace detail
192{
193using json = nlohmann::json;
194
195// keys need to be unique so using counter value to generate unique key
196static int counter = 0;
197
198// list of debug traces
199static std::vector<std::pair<std::string, std::string>> traceLog;
200
Brad Bishop63508a72020-10-27 18:55:01 -0400201void processLogTraceCallback(void*, const char* fmt, va_list ap)
Brad Bishopf6783cd2020-10-27 19:25:09 -0400202{
203 va_list vap;
204 va_copy(vap, ap);
205 std::vector<char> logData(1 + std::vsnprintf(nullptr, 0, fmt, ap));
206 std::vsnprintf(logData.data(), logData.size(), fmt, vap);
207 va_end(vap);
208 std::string logstr(logData.begin(), logData.end());
209
210 log<level::INFO>(logstr.c_str());
211
212 char timeBuf[80];
213 time_t t = time(0);
214 tm myTm{};
215 gmtime_r(&t, &myTm);
216 strftime(timeBuf, 80, "%Y-%m-%d %H:%M:%S", &myTm);
217
218 // key values need to be unique for PEL
219 // TODO #openbmc/dev/issues/1563
220 // If written to Json no need to worry about unique KEY
221 std::stringstream str;
222 str << std::setfill('0');
223 str << "LOG" << std::setw(3) << counter;
224 str << " " << timeBuf;
225 traceLog.emplace_back(std::make_pair(str.str(), std::move(logstr)));
226 counter++;
227}
228
229/**
230 * @brief GET PEL priority from pHAL priority
231 *
232 * The pHAL callout priority is in different format than PEL format
233 * so, this api is used to return current phal supported priority into
234 * PEL expected format.
235 *
236 * @param[in] phalPriority used to pass phal priority format string
237 *
238 * @return pel priority format string else empty if failure
239 *
240 * @note For "NONE" returning "L" (LOW)
241 */
242static std::string getPelPriority(const std::string& phalPriority)
243{
244 const std::map<std::string, std::string> priorityMap = {
245 {"HIGH", "H"}, {"MEDIUM", "M"}, {"LOW", "L"}, {"NONE", "L"}};
246
247 auto it = priorityMap.find(phalPriority);
248 if (it == priorityMap.end())
249 {
250 log<level::ERR>(fmt::format("Unsupported phal priority({}) is given "
251 "to get pel priority format",
252 phalPriority)
253 .c_str());
254 return "H";
255 }
256
257 return it->second;
258}
259
260void processBootErrorCallback(bool status)
261{
262 log<level::INFO>("processBootCallback ", entry("STATUS=%d", status));
263 try
264 {
265 // return If no failure during hwp execution
266 if (status)
267 return;
268
269 // Collecting ffdc details from phal
270 FFDC ffdc;
271 libekb_get_ffdc(ffdc);
272
Ramesh Iyyar3af83eb2020-11-19 23:11:38 -0600273 log<level::INFO>(
274 fmt::format("PHAL FFDC: Return Message[{}]", ffdc.message).c_str());
Brad Bishopf6783cd2020-10-27 19:25:09 -0400275
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 }
Ramesh Iyyar3af83eb2020-11-19 23:11:38 -0600425 else if ((ffdc.ffdc_type != FFDC_TYPE_NONE) &&
Brad Bishopf6783cd2020-10-27 19:25:09 -0400426 (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
Brad Bishop63508a72020-10-27 18:55:01 -0400485void pDBGLogTraceCallbackHelper(int, const char* fmt, va_list ap)
Brad Bishopf6783cd2020-10-27 19:25:09 -0400486{
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