blob: d0a187c7fb89eb60dcf03cee4949ff4cf64f94a6 [file] [log] [blame]
Matt Spinler711d51d2019-11-06 09:36:51 -06001/**
2 * Copyright © 2019 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Jayanth Othayothda9b5832021-11-05 04:19:43 -050016#include "config.h"
17
Matt Spinlercb6b0592019-07-16 15:58:51 -050018#include "pel.hpp"
19
20#include "bcd_time.hpp"
Matt Spinler386a61e2020-08-13 15:51:12 -050021#include "extended_user_data.hpp"
Matt Spinlerc63e2e82019-12-02 15:50:12 -060022#include "extended_user_header.hpp"
Matt Spinleraa659472019-10-23 09:26:48 -050023#include "failing_mtms.hpp"
Andrew Geisslerf8e750d2022-01-14 14:56:13 -060024#include "fru_identity.hpp"
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +080025#include "json_utils.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050026#include "log_id.hpp"
Matt Spinlerf1e85e22019-11-01 11:31:31 -050027#include "pel_rules.hpp"
Aatir186ce8c2019-10-20 15:13:39 -050028#include "pel_values.hpp"
Matt Spinler131870c2019-09-25 13:29:04 -050029#include "section_factory.hpp"
Matt Spinlerbd716f02019-10-15 10:54:11 -050030#include "src.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050031#include "stream.hpp"
Matt Spinlerafa857c2019-10-24 13:03:46 -050032#include "user_data_formats.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050033
Jayanth Othayoth92b20662021-11-05 00:09:15 -050034#ifdef PEL_ENABLE_PHAL
Jayanth Othayothda9b5832021-11-05 04:19:43 -050035#include "phal_service_actions.hpp"
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050036#include "sbe_ffdc_handler.hpp"
37#endif
38
Ben Tynere32b7e72021-05-18 12:38:40 -050039#include <fmt/format.h>
Matt Spinler5b289b22020-03-26 14:27:19 -050040#include <sys/stat.h>
41#include <unistd.h>
42
Matt Spinler07eefc52019-09-26 11:18:26 -050043#include <phosphor-logging/log.hpp>
44
Patrick Williams2544b412022-10-04 08:41:06 -050045#include <iostream>
46
Matt Spinlercb6b0592019-07-16 15:58:51 -050047namespace openpower
48{
49namespace pels
50{
Aatir186ce8c2019-10-20 15:13:39 -050051namespace pv = openpower::pels::pel_values;
Matt Spinler4bfc9082020-03-24 15:05:54 -050052using namespace phosphor::logging;
Matt Spinlerb8323632019-09-20 15:11:04 -050053
Matt Spinler677381b2020-01-23 10:04:29 -060054constexpr auto unknownValue = "Unknown";
55
Matt Spinler4bfc9082020-03-24 15:05:54 -050056PEL::PEL(const message::Entry& regEntry, uint32_t obmcLogID, uint64_t timestamp,
Matt Spinlerbd716f02019-10-15 10:54:11 -050057 phosphor::logging::Entry::Level severity,
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050058 const AdditionalData& additionalData, const PelFFDC& ffdcFilesIn,
Matt Spinler9d921092022-12-15 11:54:49 -060059 const DataInterfaceBase& dataIface, const JournalBase& journal)
Matt Spinlerb8323632019-09-20 15:11:04 -050060{
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050061 // No changes in input, for non SBE error related requests
62 PelFFDC ffdcFiles = ffdcFilesIn;
63
Jayanth Othayoth92b20662021-11-05 00:09:15 -050064#ifdef PEL_ENABLE_PHAL
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050065 // Add sbe ffdc processed data into ffdcfiles.
66 namespace sbe = openpower::pels::sbe;
Patrick Williamsac1ba3f2023-05-10 07:50:16 -050067 auto processReq = std::any_of(ffdcFiles.begin(), ffdcFiles.end(),
68 [](const auto& file) {
69 return file.format == UserDataFormat::custom &&
70 file.subType == sbe::sbeFFDCSubType;
71 });
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050072 // sbeFFDC can't be destroyed until the end of the PEL constructor
73 // because it needs to keep around the FFDC Files to be used below.
74 std::unique_ptr<sbe::SbeFFDC> sbeFFDCPtr;
75 if (processReq)
76 {
Patrick Williams2544b412022-10-04 08:41:06 -050077 sbeFFDCPtr = std::make_unique<sbe::SbeFFDC>(additionalData,
78 ffdcFilesIn);
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050079 const auto& sbeFFDCFiles = sbeFFDCPtr->getSbeFFDC();
80 ffdcFiles.insert(ffdcFiles.end(), sbeFFDCFiles.begin(),
81 sbeFFDCFiles.end());
Jayanth Othayoth742b00b2022-06-30 05:16:57 -050082
83 // update pel priority for spare clock failures
84 if (auto customSeverity = sbeFFDCPtr->getSeverity())
85 {
86 severity = customSeverity.value();
87 }
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050088 }
89#endif
90
Matt Spinler85f61a62020-06-03 16:28:55 -050091 std::map<std::string, std::vector<std::string>> debugData;
Matt Spinler5a90a952020-08-27 09:39:03 -050092 nlohmann::json callouts;
Matt Spinler85f61a62020-06-03 16:28:55 -050093
Matt Spinler4bfc9082020-03-24 15:05:54 -050094 _ph = std::make_unique<PrivateHeader>(regEntry.componentID, obmcLogID,
Matt Spinlerb8323632019-09-20 15:11:04 -050095 timestamp);
Vijay Lobo6b3f3452021-04-15 23:04:42 -050096 _uh = std::make_unique<UserHeader>(regEntry, severity, additionalData,
97 dataIface);
Matt Spinlerb8323632019-09-20 15:11:04 -050098
Matt Spinler5a90a952020-08-27 09:39:03 -050099 // Extract any callouts embedded in an FFDC file.
100 if (!ffdcFiles.empty())
101 {
102 try
103 {
104 callouts = getCalloutJSON(ffdcFiles);
105 }
106 catch (const std::exception& e)
107 {
108 debugData.emplace("FFDC file JSON callouts error",
109 std::vector<std::string>{e.what()});
110 }
111 }
112
Patrick Williams2544b412022-10-04 08:41:06 -0500113 auto src = std::make_unique<SRC>(regEntry, additionalData, callouts,
114 dataIface);
Matt Spinler5a90a952020-08-27 09:39:03 -0500115
Matt Spinler85f61a62020-06-03 16:28:55 -0500116 if (!src->getDebugData().empty())
117 {
118 // Something didn't go as planned
119 debugData.emplace("SRC", src->getDebugData());
120 }
Matt Spinlerc63e2e82019-12-02 15:50:12 -0600121
Matt Spinler4bfc9082020-03-24 15:05:54 -0500122 auto euh = std::make_unique<ExtendedUserHeader>(dataIface, regEntry, *src);
Matt Spinlerc63e2e82019-12-02 15:50:12 -0600123
Matt Spinlerbd716f02019-10-15 10:54:11 -0500124 _optionalSections.push_back(std::move(src));
Matt Spinlerc63e2e82019-12-02 15:50:12 -0600125 _optionalSections.push_back(std::move(euh));
Matt Spinlerb8323632019-09-20 15:11:04 -0500126
Matt Spinleraa659472019-10-23 09:26:48 -0500127 auto mtms = std::make_unique<FailingMTMS>(dataIface);
128 _optionalSections.push_back(std::move(mtms));
Matt Spinlerbd716f02019-10-15 10:54:11 -0500129
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600130 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface);
Matt Spinler85f61a62020-06-03 16:28:55 -0500131 addUserDataSection(std::move(ud));
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600132
Sumit Kumar3e274432021-09-14 06:37:56 -0500133 // Check for pel severity of type - 0x51 = critical error, system
134 // termination and update terminate bit in SRC for pels
135 updateTerminateBitInSRCSection();
136
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500137 // Create a UserData section from AdditionalData.
Matt Spinlerafa857c2019-10-24 13:03:46 -0500138 if (!additionalData.empty())
139 {
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600140 ud = util::makeADUserDataSection(additionalData);
Matt Spinler85f61a62020-06-03 16:28:55 -0500141 addUserDataSection(std::move(ud));
Matt Spinlerafa857c2019-10-24 13:03:46 -0500142 }
143
Matt Spinler56ad2a02020-03-26 14:00:52 -0500144 // Add any FFDC files into UserData sections
145 for (const auto& file : ffdcFiles)
146 {
147 ud = util::makeFFDCuserDataSection(regEntry.componentID, file);
148 if (!ud)
149 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500150 // Add this error into the debug data UserData section
151 std::ostringstream msg;
152 msg << "Could not make PEL FFDC UserData section from file"
153 << std::hex << regEntry.componentID << " " << file.subType
154 << " " << file.version;
155 if (debugData.count("FFDC File"))
156 {
157 debugData.at("FFDC File").push_back(msg.str());
158 }
159 else
160 {
161 debugData.emplace("FFDC File",
162 std::vector<std::string>{msg.str()});
163 }
164
Matt Spinler56ad2a02020-03-26 14:00:52 -0500165 continue;
166 }
167
Matt Spinler85f61a62020-06-03 16:28:55 -0500168 addUserDataSection(std::move(ud));
169 }
Matt Spinler56ad2a02020-03-26 14:00:52 -0500170
Jayanth Othayothda9b5832021-11-05 04:19:43 -0500171#ifdef PEL_ENABLE_PHAL
172 auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID);
Jayanth Othayoth3ef7b602021-11-09 06:40:38 -0600173 openpower::pels::phal::createServiceActions(callouts, path, dataIface,
174 plid());
Jayanth Othayothda9b5832021-11-05 04:19:43 -0500175#endif
176
Matt Spinler85f61a62020-06-03 16:28:55 -0500177 // Store in the PEL any important debug data created while
178 // building the PEL sections.
179 if (!debugData.empty())
180 {
181 nlohmann::json data;
182 data["PEL Internal Debug Data"] = debugData;
183 ud = util::makeJSONUserDataSection(data);
184
185 addUserDataSection(std::move(ud));
186
187 // Also put in the journal for debug
Matt Spinler45796e82022-07-01 11:25:27 -0500188 for (const auto& [name, msgs] : debugData)
Matt Spinler85f61a62020-06-03 16:28:55 -0500189 {
Matt Spinler45796e82022-07-01 11:25:27 -0500190 for (const auto& message : msgs)
Matt Spinler85f61a62020-06-03 16:28:55 -0500191 {
192 std::string entry = name + ": " + message;
193 log<level::INFO>(entry.c_str());
Matt Spinler56ad2a02020-03-26 14:00:52 -0500194 }
195 }
Matt Spinler56ad2a02020-03-26 14:00:52 -0500196 }
197
Matt Spinler9d921092022-12-15 11:54:49 -0600198 addJournalSections(regEntry, journal);
199
Matt Spinler97d19b42019-10-29 11:34:03 -0500200 _ph->setSectionCount(2 + _optionalSections.size());
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500201
202 checkRulesAndFix();
Matt Spinlerb8323632019-09-20 15:11:04 -0500203}
Matt Spinlercb6b0592019-07-16 15:58:51 -0500204
Patrick Williams2544b412022-10-04 08:41:06 -0500205PEL::PEL(std::vector<uint8_t>& data) : PEL(data, 0) {}
Matt Spinlercb6b0592019-07-16 15:58:51 -0500206
Matt Spinler07eefc52019-09-26 11:18:26 -0500207PEL::PEL(std::vector<uint8_t>& data, uint32_t obmcLogID)
Matt Spinlercb6b0592019-07-16 15:58:51 -0500208{
Matt Spinler07eefc52019-09-26 11:18:26 -0500209 populateFromRawData(data, obmcLogID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500210}
211
Matt Spinler07eefc52019-09-26 11:18:26 -0500212void PEL::populateFromRawData(std::vector<uint8_t>& data, uint32_t obmcLogID)
Matt Spinlercb6b0592019-07-16 15:58:51 -0500213{
Matt Spinler07eefc52019-09-26 11:18:26 -0500214 Stream pelData{data};
Matt Spinlercb6b0592019-07-16 15:58:51 -0500215 _ph = std::make_unique<PrivateHeader>(pelData);
216 if (obmcLogID != 0)
217 {
Matt Spinler97d19b42019-10-29 11:34:03 -0500218 _ph->setOBMCLogID(obmcLogID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500219 }
220
221 _uh = std::make_unique<UserHeader>(pelData);
Matt Spinler131870c2019-09-25 13:29:04 -0500222
223 // Use the section factory to create the rest of the objects
224 for (size_t i = 2; i < _ph->sectionCount(); i++)
225 {
226 auto section = section_factory::create(pelData);
227 _optionalSections.push_back(std::move(section));
228 }
Matt Spinlercb6b0592019-07-16 15:58:51 -0500229}
230
231bool PEL::valid() const
232{
233 bool valid = _ph->valid();
234
235 if (valid)
236 {
237 valid = _uh->valid();
238 }
239
Matt Spinler131870c2019-09-25 13:29:04 -0500240 if (valid)
241 {
242 if (!std::all_of(_optionalSections.begin(), _optionalSections.end(),
243 [](const auto& section) { return section->valid(); }))
244 {
245 valid = false;
246 }
247 }
248
Matt Spinlercb6b0592019-07-16 15:58:51 -0500249 return valid;
250}
251
252void PEL::setCommitTime()
253{
254 auto now = std::chrono::system_clock::now();
Matt Spinler97d19b42019-10-29 11:34:03 -0500255 _ph->setCommitTimestamp(getBCDTime(now));
Matt Spinlercb6b0592019-07-16 15:58:51 -0500256}
257
258void PEL::assignID()
259{
Matt Spinler97d19b42019-10-29 11:34:03 -0500260 _ph->setID(generatePELID());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500261}
262
Matt Spinler06885452019-11-06 10:35:42 -0600263void PEL::flatten(std::vector<uint8_t>& pelBuffer) const
Matt Spinlercb6b0592019-07-16 15:58:51 -0500264{
265 Stream pelData{pelBuffer};
Matt Spinlerb8323632019-09-20 15:11:04 -0500266
Matt Spinler07eefc52019-09-26 11:18:26 -0500267 if (!valid())
Matt Spinlercb6b0592019-07-16 15:58:51 -0500268 {
Matt Spinler07eefc52019-09-26 11:18:26 -0500269 log<level::WARNING>("Unflattening an invalid PEL");
Matt Spinlercb6b0592019-07-16 15:58:51 -0500270 }
271
Matt Spinler07eefc52019-09-26 11:18:26 -0500272 _ph->flatten(pelData);
Matt Spinlerb8323632019-09-20 15:11:04 -0500273 _uh->flatten(pelData);
Matt Spinler07eefc52019-09-26 11:18:26 -0500274
275 for (auto& section : _optionalSections)
276 {
277 section->flatten(pelData);
278 }
Matt Spinlercb6b0592019-07-16 15:58:51 -0500279}
280
Matt Spinler06885452019-11-06 10:35:42 -0600281std::vector<uint8_t> PEL::data() const
Matt Spinlercb6b0592019-07-16 15:58:51 -0500282{
Matt Spinler07eefc52019-09-26 11:18:26 -0500283 std::vector<uint8_t> pelData;
284 flatten(pelData);
285 return pelData;
Matt Spinlercb6b0592019-07-16 15:58:51 -0500286}
287
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600288size_t PEL::size() const
289{
290 size_t size = 0;
291
292 if (_ph)
293 {
294 size += _ph->header().size;
295 }
296
297 if (_uh)
298 {
299 size += _uh->header().size;
300 }
301
302 for (const auto& section : _optionalSections)
303 {
304 size += section->header().size;
305 }
306
307 return size;
308}
309
Matt Spinlerbd716f02019-10-15 10:54:11 -0500310std::optional<SRC*> PEL::primarySRC() const
311{
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500312 auto src = std::find_if(_optionalSections.begin(), _optionalSections.end(),
313 [](auto& section) {
314 return section->header().id ==
315 static_cast<uint16_t>(SectionID::primarySRC);
316 });
Matt Spinlerbd716f02019-10-15 10:54:11 -0500317 if (src != _optionalSections.end())
318 {
319 return static_cast<SRC*>(src->get());
320 }
321
322 return std::nullopt;
323}
324
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500325void PEL::checkRulesAndFix()
326{
Matt Spinler1f93c592020-09-10 10:43:08 -0500327 // Only fix if the action flags are at their default value which
328 // means they weren't specified in the registry. Otherwise
329 // assume the user knows what they are doing.
330 if (_uh->actionFlags() == actionFlagsDefault)
331 {
Patrick Williams2544b412022-10-04 08:41:06 -0500332 auto [actionFlags, eventType] = pel_rules::check(0, _uh->eventType(),
333 _uh->severity());
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500334
Matt Spinler1f93c592020-09-10 10:43:08 -0500335 _uh->setActionFlags(actionFlags);
336 _uh->setEventType(eventType);
337 }
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500338}
339
Matt Spinleracb7c102020-01-10 13:49:22 -0600340void PEL::printSectionInJSON(const Section& section, std::string& buf,
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800341 std::map<uint16_t, size_t>& pluralSections,
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800342 message::Registry& registry,
343 const std::vector<std::string>& plugins,
344 uint8_t creatorID) const
Aatir186ce8c2019-10-20 15:13:39 -0500345{
346 char tmpB[5];
Aatir Manzurad0e0472019-10-07 13:18:37 -0500347 uint8_t id[] = {static_cast<uint8_t>(section.header().id >> 8),
348 static_cast<uint8_t>(section.header().id)};
349 sprintf(tmpB, "%c%c", id[0], id[1]);
350 std::string sectionID(tmpB);
351 std::string sectionName = pv::sectionTitles.count(sectionID)
352 ? pv::sectionTitles.at(sectionID)
353 : "Unknown Section";
Matt Spinleracb7c102020-01-10 13:49:22 -0600354
355 // Add a count if there are multiple of this type of section
356 auto count = pluralSections.find(section.header().id);
357 if (count != pluralSections.end())
358 {
359 sectionName += " " + std::to_string(count->second);
360 count->second++;
361 }
362
Aatir186ce8c2019-10-20 15:13:39 -0500363 if (section.valid())
364 {
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800365 std::optional<std::string> json;
366 if (sectionID == "PS" || sectionID == "SS")
367 {
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800368 json = section.getJSON(registry, plugins, creatorID);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800369 }
Matt Spinler386a61e2020-08-13 15:51:12 -0500370 else if ((sectionID == "UD") || (sectionID == "ED"))
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800371 {
Harisuddin Mohamed Isa3fdcd4e2020-08-26 11:56:42 +0800372 json = section.getJSON(creatorID, plugins);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800373 }
374 else
375 {
Matt Spinlerb832aa52023-03-21 15:32:34 -0500376 json = section.getJSON(creatorID);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800377 }
Matt Spinler4220a152020-03-26 10:18:09 -0500378
379 buf += "\"" + sectionName + "\": {\n";
380
Aatir Manzurad0e0472019-10-07 13:18:37 -0500381 if (json)
382 {
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800383 buf += *json + "\n},\n";
Aatir Manzurad0e0472019-10-07 13:18:37 -0500384 }
385 else
386 {
Matt Spinler4220a152020-03-26 10:18:09 -0500387 jsonInsert(buf, pv::sectionVer,
388 getNumberString("%d", section.header().version), 1);
389 jsonInsert(buf, pv::subSection,
390 getNumberString("%d", section.header().subType), 1);
391 jsonInsert(buf, pv::createdBy,
392 getNumberString("0x%X", section.header().componentID),
393 1);
394
Aatir Manzurad0e0472019-10-07 13:18:37 -0500395 std::vector<uint8_t> data;
396 Stream s{data};
397 section.flatten(s);
Matt Spinler4220a152020-03-26 10:18:09 -0500398 std::string dstr =
399 dumpHex(std::data(data) + SectionHeader::flattenedSize(),
Harisuddin Mohamed Isa097ad122020-06-11 21:19:41 +0800400 data.size() - SectionHeader::flattenedSize(), 2);
Matt Spinler4220a152020-03-26 10:18:09 -0500401 std::string jsonIndent(indentLevel, 0x20);
402 buf += jsonIndent + "\"Data\": [\n";
403 buf += dstr;
404 buf += jsonIndent + "]\n";
405 buf += "},\n";
Aatir Manzurad0e0472019-10-07 13:18:37 -0500406 }
Aatir186ce8c2019-10-20 15:13:39 -0500407 }
408 else
409 {
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800410 buf += "\n\"Invalid Section\": [\n \"invalid\"\n],\n";
Aatir186ce8c2019-10-20 15:13:39 -0500411 }
412}
413
Matt Spinleracb7c102020-01-10 13:49:22 -0600414std::map<uint16_t, size_t> PEL::getPluralSections() const
415{
416 std::map<uint16_t, size_t> sectionCounts;
417
418 for (const auto& section : optionalSections())
419 {
420 if (sectionCounts.find(section->header().id) == sectionCounts.end())
421 {
422 sectionCounts[section->header().id] = 1;
423 }
424 else
425 {
426 sectionCounts[section->header().id]++;
427 }
428 }
429
430 std::map<uint16_t, size_t> sections;
431 for (const auto& [id, count] : sectionCounts)
432 {
433 if (count > 1)
434 {
435 // Start with 0 here and printSectionInJSON()
436 // will increment it as it goes.
437 sections.emplace(id, 0);
438 }
439 }
440
441 return sections;
442}
443
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800444void PEL::toJSON(message::Registry& registry,
445 const std::vector<std::string>& plugins) const
Aatir186ce8c2019-10-20 15:13:39 -0500446{
Matt Spinleracb7c102020-01-10 13:49:22 -0600447 auto sections = getPluralSections();
448
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800449 std::string buf = "{\n";
Matt Spinlerb832aa52023-03-21 15:32:34 -0500450 printSectionInJSON(*(_ph.get()), buf, sections, registry, plugins,
451 _ph->creatorID());
452 printSectionInJSON(*(_uh.get()), buf, sections, registry, plugins,
453 _ph->creatorID());
Aatir186ce8c2019-10-20 15:13:39 -0500454 for (auto& section : this->optionalSections())
455 {
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800456 printSectionInJSON(*(section.get()), buf, sections, registry, plugins,
457 _ph->creatorID());
Aatir186ce8c2019-10-20 15:13:39 -0500458 }
459 buf += "}";
460 std::size_t found = buf.rfind(",");
461 if (found != std::string::npos)
462 buf.replace(found, 1, "");
463 std::cout << buf << std::endl;
464}
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800465
Matt Spinler85f61a62020-06-03 16:28:55 -0500466bool PEL::addUserDataSection(std::unique_ptr<UserData> userData)
467{
468 if (size() + userData->header().size > _maxPELSize)
469 {
470 if (userData->shrink(_maxPELSize - size()))
471 {
472 _optionalSections.push_back(std::move(userData));
473 }
474 else
475 {
476 log<level::WARNING>(
477 "Could not shrink UserData section. Dropping",
478 entry("SECTION_SIZE=%d\n", userData->header().size),
479 entry("COMPONENT_ID=0x%02X", userData->header().componentID),
480 entry("SUBTYPE=0x%X", userData->header().subType),
481 entry("VERSION=0x%X", userData->header().version));
482 return false;
483 }
484 }
485 else
486 {
487 _optionalSections.push_back(std::move(userData));
488 }
489 return true;
490}
491
Matt Spinler5a90a952020-08-27 09:39:03 -0500492nlohmann::json PEL::getCalloutJSON(const PelFFDC& ffdcFiles)
493{
494 nlohmann::json callouts;
495
496 for (const auto& file : ffdcFiles)
497 {
498 if ((file.format == UserDataFormat::json) &&
499 (file.subType == jsonCalloutSubtype))
500 {
501 auto data = util::readFD(file.fd);
502 if (data.empty())
503 {
504 throw std::runtime_error{
505 "Could not get data from JSON callout file descriptor"};
506 }
507
508 std::string jsonString{data.begin(), data.begin() + data.size()};
509
510 callouts = nlohmann::json::parse(jsonString);
511 break;
512 }
513 }
514
515 return callouts;
516}
517
Andrew Geisslerf8e750d2022-01-14 14:56:13 -0600518bool PEL::isHwCalloutPresent() const
Andrew Geissler44fc3162020-07-09 09:21:31 -0500519{
520 auto pSRC = primarySRC();
521 if (!pSRC)
522 {
523 return false;
524 }
525
526 bool calloutPresent = false;
527 if ((*pSRC)->callouts())
528 {
529 for (auto& i : (*pSRC)->callouts()->callouts())
530 {
531 if (((*i).fruIdentity()))
532 {
533 auto& fruId = (*i).fruIdentity();
Andrew Geisslerf8e750d2022-01-14 14:56:13 -0600534 if ((*fruId).failingComponentType() ==
535 src::FRUIdentity::hardwareFRU)
Andrew Geissler44fc3162020-07-09 09:21:31 -0500536 {
537 calloutPresent = true;
538 break;
539 }
540 }
541 }
542 }
543
544 return calloutPresent;
545}
546
Sumit Kumar3160a542021-04-26 08:07:04 -0500547void PEL::updateSysInfoInExtendedUserDataSection(
548 const DataInterfaceBase& dataIface)
549{
550 const AdditionalData additionalData;
551
552 // Check for PEL from Hostboot
553 if (_ph->creatorID() == static_cast<uint8_t>(CreatorID::hostboot))
554 {
555 // Get the ED section from PEL
556 auto op = std::find_if(_optionalSections.begin(),
Patrick Williamsac1ba3f2023-05-10 07:50:16 -0500557 _optionalSections.end(),
558 [](auto& section) {
559 return section->header().id ==
560 static_cast<uint16_t>(SectionID::extUserData);
561 });
Sumit Kumar3160a542021-04-26 08:07:04 -0500562
563 // Check for ED section found and its not the last section of PEL
564 if (op != _optionalSections.end())
565 {
566 // Get the extended user data class mapped to found section
567 auto extUserData = static_cast<ExtendedUserData*>(op->get());
568
569 // Check for the creator ID is for OpenBMC
570 if (extUserData->creatorID() ==
571 static_cast<uint8_t>(CreatorID::openBMC))
572 {
573 // Update subtype and component id
574 auto subType = static_cast<uint8_t>(UserDataFormat::json);
575 auto componentId =
576 static_cast<uint16_t>(ComponentID::phosphorLogging);
577
578 // Update system data to ED section
George Liu9ac0d9b2022-07-15 10:57:38 +0800579 auto ud = util::makeSysInfoUserDataSection(additionalData,
580 dataIface, false);
Sumit Kumar3160a542021-04-26 08:07:04 -0500581 extUserData->updateDataSection(subType, componentId,
582 ud->data());
583 }
584 }
585 }
586}
587
Matt Spinler8e65f4e2023-05-02 13:40:08 -0500588bool PEL::getDeconfigFlag() const
589{
590 auto creator = static_cast<CreatorID>(_ph->creatorID());
591
592 if ((creator == CreatorID::openBMC) || (creator == CreatorID::hostboot))
593 {
594 auto src = primarySRC();
595 return (*src)->getErrorStatusFlag(SRC::ErrorStatusFlags::deconfigured);
596 }
597 return false;
598}
599
600bool PEL::getGuardFlag() const
601{
602 auto creator = static_cast<CreatorID>(_ph->creatorID());
603
604 if ((creator == CreatorID::openBMC) || (creator == CreatorID::hostboot))
605 {
606 auto src = primarySRC();
607 return (*src)->getErrorStatusFlag(SRC::ErrorStatusFlags::guarded);
608 }
609 return false;
610}
611
Sumit Kumar3e274432021-09-14 06:37:56 -0500612void PEL::updateTerminateBitInSRCSection()
613{
614 // Check for pel severity of type - 0x51 = critical error, system
615 // termination
616 if (_uh->severity() == 0x51)
617 {
618 // Get the primary SRC section
619 auto pSRC = primarySRC();
620 if (pSRC)
621 {
622 (*pSRC)->setTerminateBit();
623 }
624 }
625}
626
Matt Spinler9d921092022-12-15 11:54:49 -0600627void PEL::addJournalSections(const message::Entry& regEntry,
628 const JournalBase& journal)
629{
630 if (!regEntry.journalCapture)
631 {
632 return;
633 }
634
635 // Write all unwritten journal data to disk.
636 journal.sync();
637
638 const auto& jc = regEntry.journalCapture.value();
639 std::vector<std::vector<std::string>> allMessages;
640
641 if (std::holds_alternative<size_t>(jc))
642 {
643 // Get the previous numLines journal entries
644 const auto& numLines = std::get<size_t>(jc);
645 try
646 {
647 auto messages = journal.getMessages("", numLines);
648 if (!messages.empty())
649 {
650 allMessages.push_back(std::move(messages));
651 }
652 }
653 catch (const std::exception& e)
654 {
655 log<level::ERR>(
656 fmt::format("Failed during journal collection: {}", e.what())
657 .c_str());
658 }
659 }
660 else if (std::holds_alternative<message::AppCaptureList>(jc))
661 {
662 // Get journal entries based on the syslog id field.
663 const auto& sections = std::get<message::AppCaptureList>(jc);
664 for (const auto& [syslogID, numLines] : sections)
665 {
666 try
667 {
668 auto messages = journal.getMessages(syslogID, numLines);
669 if (!messages.empty())
670 {
671 allMessages.push_back(std::move(messages));
672 }
673 }
674 catch (const std::exception& e)
675 {
676 log<level::ERR>(
677 fmt::format("Failed during journal collection: {}",
678 e.what())
679 .c_str());
680 }
681 }
682 }
683
684 // Create the UserData sections
685 for (const auto& messages : allMessages)
686 {
687 auto buffer = util::flattenLines(messages);
688
689 // If the buffer is way too big, it can overflow the uint16_t
690 // PEL section size field that is checked below so do a cursory
691 // check here.
692 if (buffer.size() > _maxPELSize)
693 {
694 log<level::WARNING>(
695 "Journal UserData section does not fit in PEL, dropping");
696 log<level::WARNING>(fmt::format("PEL size = {}, data size = {}",
697 size(), buffer.size())
698 .c_str());
699 continue;
700 }
701
702 // Sections must be 4 byte aligned.
703 while (buffer.size() % 4 != 0)
704 {
705 buffer.push_back(0);
706 }
707
708 auto ud = std::make_unique<UserData>(
709 static_cast<uint16_t>(ComponentID::phosphorLogging),
710 static_cast<uint8_t>(UserDataFormat::text),
711 static_cast<uint8_t>(UserDataFormatVersion::text), buffer);
712
713 if (size() + ud->header().size <= _maxPELSize)
714 {
715 _optionalSections.push_back(std::move(ud));
716 }
717 else
718 {
719 // Don't attempt to shrink here since we'd be dropping the
720 // most recent journal entries which would be confusing.
721 log<level::WARNING>(
722 "Journal UserData section does not fit in PEL, dropping");
723 log<level::WARNING>(fmt::format("PEL size = {}, UserData size = {}",
724 size(), ud->header().size)
725 .c_str());
726 ud.reset();
727 continue;
728 }
729 }
730}
731
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600732namespace util
733{
734
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600735std::unique_ptr<UserData> makeJSONUserDataSection(const nlohmann::json& json)
736{
737 auto jsonString = json.dump();
738 std::vector<uint8_t> jsonData(jsonString.begin(), jsonString.end());
739
740 // Pad to a 4 byte boundary
741 while ((jsonData.size() % 4) != 0)
742 {
743 jsonData.push_back(0);
744 }
745
746 return std::make_unique<UserData>(
747 static_cast<uint16_t>(ComponentID::phosphorLogging),
748 static_cast<uint8_t>(UserDataFormat::json),
749 static_cast<uint8_t>(UserDataFormatVersion::json), jsonData);
750}
751
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600752std::unique_ptr<UserData> makeADUserDataSection(const AdditionalData& ad)
753{
754 assert(!ad.empty());
755 nlohmann::json json;
756
757 // Remove the 'ESEL' entry, as it contains a full PEL in the value.
758 if (ad.getValue("ESEL"))
759 {
760 auto newAD = ad;
761 newAD.remove("ESEL");
762 json = newAD.toJSON();
763 }
764 else
765 {
766 json = ad.toJSON();
767 }
768
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600769 return makeJSONUserDataSection(json);
770}
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600771
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600772void addProcessNameToJSON(nlohmann::json& json,
773 const std::optional<std::string>& pid,
774 const DataInterfaceBase& dataIface)
775{
Matt Spinler677381b2020-01-23 10:04:29 -0600776 std::string name{unknownValue};
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600777
778 try
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600779 {
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600780 if (pid)
781 {
782 auto n = dataIface.getProcessName(*pid);
783 if (n)
784 {
785 name = *n;
786 }
787 }
788 }
Patrick Williams66491c62021-10-06 12:23:37 -0500789 catch (const std::exception& e)
Patrick Williams2544b412022-10-04 08:41:06 -0500790 {}
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600791
Sumit Kumar3160a542021-04-26 08:07:04 -0500792 if (pid)
793 {
794 json["Process Name"] = std::move(name);
795 }
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600796}
797
Matt Spinler677381b2020-01-23 10:04:29 -0600798void addBMCFWVersionIDToJSON(nlohmann::json& json,
799 const DataInterfaceBase& dataIface)
800{
801 auto id = dataIface.getBMCFWVersionID();
802 if (id.empty())
803 {
804 id = unknownValue;
805 }
806
Matt Spinlerc2b8a512021-05-21 12:44:42 -0600807 json["FW Version ID"] = std::move(id);
Matt Spinler677381b2020-01-23 10:04:29 -0600808}
809
Matt Spinler4aa23a12020-02-03 15:05:09 -0600810std::string lastSegment(char separator, std::string data)
811{
812 auto pos = data.find_last_of(separator);
813 if (pos != std::string::npos)
814 {
815 data = data.substr(pos + 1);
816 }
817
818 return data;
819}
820
Ben Tynere32b7e72021-05-18 12:38:40 -0500821void addIMKeyword(nlohmann::json& json, const DataInterfaceBase& dataIface)
822{
823 auto keyword = dataIface.getSystemIMKeyword();
824
825 std::string value{};
826
827 std::for_each(keyword.begin(), keyword.end(), [&](const auto& byte) {
828 value += fmt::format("{:02X}", byte);
829 });
830
831 json["System IM"] = value;
832}
833
Matt Spinler4aa23a12020-02-03 15:05:09 -0600834void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface)
835{
836 json["BMCState"] = lastSegment('.', dataIface.getBMCState());
837 json["ChassisState"] = lastSegment('.', dataIface.getChassisState());
838 json["HostState"] = lastSegment('.', dataIface.getHostState());
Sumit Kumar2c36fdd2021-09-21 03:12:11 -0500839 json["BootState"] = lastSegment('.', dataIface.getBootState());
Matt Spinler4aa23a12020-02-03 15:05:09 -0600840}
841
George Liu9ac0d9b2022-07-15 10:57:38 +0800842void addBMCUptime(nlohmann::json& json, const DataInterfaceBase& dataIface)
843{
844 auto seconds = dataIface.getUptimeInSeconds();
845 if (seconds)
846 {
847 json["BMCUptime"] = dataIface.getBMCUptime(*seconds);
848 }
849 else
850 {
851 json["BMCUptime"] = "";
852 }
853 json["BMCLoad"] = dataIface.getBMCLoadAvg();
854}
855
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600856std::unique_ptr<UserData>
857 makeSysInfoUserDataSection(const AdditionalData& ad,
George Liu9ac0d9b2022-07-15 10:57:38 +0800858 const DataInterfaceBase& dataIface,
859 bool addUptime)
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600860{
861 nlohmann::json json;
862
863 addProcessNameToJSON(json, ad.getValue("_PID"), dataIface);
Matt Spinler677381b2020-01-23 10:04:29 -0600864 addBMCFWVersionIDToJSON(json, dataIface);
Ben Tynere32b7e72021-05-18 12:38:40 -0500865 addIMKeyword(json, dataIface);
Matt Spinler4aa23a12020-02-03 15:05:09 -0600866 addStatesToJSON(json, dataIface);
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600867
George Liu9ac0d9b2022-07-15 10:57:38 +0800868 if (addUptime)
869 {
870 addBMCUptime(json, dataIface);
871 }
872
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600873 return makeJSONUserDataSection(json);
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600874}
875
Matt Spinler5b289b22020-03-26 14:27:19 -0500876std::vector<uint8_t> readFD(int fd)
877{
878 std::vector<uint8_t> data;
879
880 // Get the size
881 struct stat s;
882 int r = fstat(fd, &s);
883 if (r != 0)
884 {
885 auto e = errno;
886 log<level::ERR>("Could not get FFDC file size from FD",
887 entry("ERRNO=%d", e));
888 return data;
889 }
890
891 if (0 == s.st_size)
892 {
893 log<level::ERR>("FFDC file is empty");
894 return data;
895 }
896
897 data.resize(s.st_size);
898
899 // Make sure its at the beginning, as maybe another
900 // extension already used it.
901 r = lseek(fd, 0, SEEK_SET);
902 if (r == -1)
903 {
904 auto e = errno;
905 log<level::ERR>("Could not seek to beginning of FFDC file",
906 entry("ERRNO=%d", e));
907 return data;
908 }
909
910 r = read(fd, data.data(), s.st_size);
911 if (r == -1)
912 {
913 auto e = errno;
914 log<level::ERR>("Could not read FFDC file", entry("ERRNO=%d", e));
915 }
916 else if (r != s.st_size)
917 {
918 log<level::WARNING>("Could not read full FFDC file",
919 entry("FILE_SIZE=%d", s.st_size),
920 entry("SIZE_READ=%d", r));
921 }
922
923 return data;
924}
925
Matt Spinler56ad2a02020-03-26 14:00:52 -0500926std::unique_ptr<UserData> makeFFDCuserDataSection(uint16_t componentID,
927 const PelFFDCfile& file)
928{
Matt Spinler5b289b22020-03-26 14:27:19 -0500929 auto data = readFD(file.fd);
930
931 if (data.empty())
932 {
933 return std::unique_ptr<UserData>();
934 }
935
936 // The data needs 4 Byte alignment, and save amount padded for the
937 // CBOR case.
938 uint32_t pad = 0;
939 while (data.size() % 4)
940 {
941 data.push_back(0);
942 pad++;
943 }
944
945 // For JSON, CBOR, and Text use our component ID, subType, and version,
946 // otherwise use the supplied ones.
947 uint16_t compID = static_cast<uint16_t>(ComponentID::phosphorLogging);
948 uint8_t subType{};
949 uint8_t version{};
950
951 switch (file.format)
952 {
953 case UserDataFormat::json:
954 subType = static_cast<uint8_t>(UserDataFormat::json);
955 version = static_cast<uint8_t>(UserDataFormatVersion::json);
956 break;
957 case UserDataFormat::cbor:
958 subType = static_cast<uint8_t>(UserDataFormat::cbor);
959 version = static_cast<uint8_t>(UserDataFormatVersion::cbor);
960
961 // The CBOR parser will fail on the extra pad bytes since they
962 // aren't CBOR. Add the amount we padded to the end and other
963 // code will remove it all before parsing.
964 {
965 data.resize(data.size() + 4);
966 Stream stream{data};
967 stream.offset(data.size() - 4);
968 stream << pad;
969 }
970
971 break;
972 case UserDataFormat::text:
973 subType = static_cast<uint8_t>(UserDataFormat::text);
974 version = static_cast<uint8_t>(UserDataFormatVersion::text);
975 break;
976 case UserDataFormat::custom:
977 default:
978 // Use the passed in values
979 compID = componentID;
980 subType = file.subType;
981 version = file.version;
982 break;
983 }
984
985 return std::make_unique<UserData>(compID, subType, version, data);
Matt Spinler56ad2a02020-03-26 14:00:52 -0500986}
987
Matt Spinler9d921092022-12-15 11:54:49 -0600988std::vector<uint8_t> flattenLines(const std::vector<std::string>& lines)
989{
990 std::vector<uint8_t> out;
991
992 for (const auto& line : lines)
993 {
994 out.insert(out.end(), line.begin(), line.end());
995
996 if (out.back() != '\n')
997 {
998 out.push_back('\n');
999 }
1000 }
1001
1002 return out;
1003}
1004
Matt Spinlerc7c3e402020-01-22 15:07:25 -06001005} // namespace util
1006
Matt Spinlercb6b0592019-07-16 15:58:51 -05001007} // namespace pels
1008} // namespace openpower