blob: 3c2744742567aead9652a8fa1d03d78a3c4c47da [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 */
Matt Spinlercb6b0592019-07-16 15:58:51 -050016#include "pel.hpp"
17
18#include "bcd_time.hpp"
Matt Spinler386a61e2020-08-13 15:51:12 -050019#include "extended_user_data.hpp"
Matt Spinlerc63e2e82019-12-02 15:50:12 -060020#include "extended_user_header.hpp"
Matt Spinleraa659472019-10-23 09:26:48 -050021#include "failing_mtms.hpp"
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +080022#include "json_utils.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050023#include "log_id.hpp"
Matt Spinlerf1e85e22019-11-01 11:31:31 -050024#include "pel_rules.hpp"
Aatir186ce8c2019-10-20 15:13:39 -050025#include "pel_values.hpp"
Matt Spinler131870c2019-09-25 13:29:04 -050026#include "section_factory.hpp"
Matt Spinlerbd716f02019-10-15 10:54:11 -050027#include "src.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050028#include "stream.hpp"
Matt Spinlerafa857c2019-10-24 13:03:46 -050029#include "user_data_formats.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050030
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050031#ifdef SBE_FFDC_SUPPORTED
32#include "sbe_ffdc_handler.hpp"
33#endif
34
Ben Tynere32b7e72021-05-18 12:38:40 -050035#include <fmt/format.h>
Matt Spinler5b289b22020-03-26 14:27:19 -050036#include <sys/stat.h>
37#include <unistd.h>
38
Aatir186ce8c2019-10-20 15:13:39 -050039#include <iostream>
Matt Spinler07eefc52019-09-26 11:18:26 -050040#include <phosphor-logging/log.hpp>
41
Matt Spinlercb6b0592019-07-16 15:58:51 -050042namespace openpower
43{
44namespace pels
45{
Matt Spinlerb8323632019-09-20 15:11:04 -050046namespace message = openpower::pels::message;
Aatir186ce8c2019-10-20 15:13:39 -050047namespace pv = openpower::pels::pel_values;
Matt Spinler4bfc9082020-03-24 15:05:54 -050048using namespace phosphor::logging;
Matt Spinlerb8323632019-09-20 15:11:04 -050049
Matt Spinler677381b2020-01-23 10:04:29 -060050constexpr auto unknownValue = "Unknown";
51
Matt Spinler4bfc9082020-03-24 15:05:54 -050052PEL::PEL(const message::Entry& regEntry, uint32_t obmcLogID, uint64_t timestamp,
Matt Spinlerbd716f02019-10-15 10:54:11 -050053 phosphor::logging::Entry::Level severity,
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050054 const AdditionalData& additionalData, const PelFFDC& ffdcFilesIn,
Matt Spinleraa659472019-10-23 09:26:48 -050055 const DataInterfaceBase& dataIface)
Matt Spinlerb8323632019-09-20 15:11:04 -050056{
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050057 // No changes in input, for non SBE error related requests
58 PelFFDC ffdcFiles = ffdcFilesIn;
59
60#ifdef SBE_FFDC_SUPPORTED
61 // Add sbe ffdc processed data into ffdcfiles.
62 namespace sbe = openpower::pels::sbe;
63 auto processReq =
64 std::any_of(ffdcFiles.begin(), ffdcFiles.end(), [](const auto& file) {
65 return file.format == UserDataFormat::custom &&
66 file.subType == sbe::sbeFFDCSubType;
67 });
68 // sbeFFDC can't be destroyed until the end of the PEL constructor
69 // because it needs to keep around the FFDC Files to be used below.
70 std::unique_ptr<sbe::SbeFFDC> sbeFFDCPtr;
71 if (processReq)
72 {
73 sbeFFDCPtr =
74 std::make_unique<sbe::SbeFFDC>(additionalData, ffdcFilesIn);
75 const auto& sbeFFDCFiles = sbeFFDCPtr->getSbeFFDC();
76 ffdcFiles.insert(ffdcFiles.end(), sbeFFDCFiles.begin(),
77 sbeFFDCFiles.end());
78 }
79#endif
80
Matt Spinler85f61a62020-06-03 16:28:55 -050081 std::map<std::string, std::vector<std::string>> debugData;
Matt Spinler5a90a952020-08-27 09:39:03 -050082 nlohmann::json callouts;
Matt Spinler85f61a62020-06-03 16:28:55 -050083
Matt Spinler4bfc9082020-03-24 15:05:54 -050084 _ph = std::make_unique<PrivateHeader>(regEntry.componentID, obmcLogID,
Matt Spinlerb8323632019-09-20 15:11:04 -050085 timestamp);
Vijay Lobo6b3f3452021-04-15 23:04:42 -050086 _uh = std::make_unique<UserHeader>(regEntry, severity, additionalData,
87 dataIface);
Matt Spinlerb8323632019-09-20 15:11:04 -050088
Matt Spinler5a90a952020-08-27 09:39:03 -050089 // Extract any callouts embedded in an FFDC file.
90 if (!ffdcFiles.empty())
91 {
92 try
93 {
94 callouts = getCalloutJSON(ffdcFiles);
95 }
96 catch (const std::exception& e)
97 {
98 debugData.emplace("FFDC file JSON callouts error",
99 std::vector<std::string>{e.what()});
100 }
101 }
102
103 auto src =
104 std::make_unique<SRC>(regEntry, additionalData, callouts, dataIface);
105
Matt Spinler85f61a62020-06-03 16:28:55 -0500106 if (!src->getDebugData().empty())
107 {
108 // Something didn't go as planned
109 debugData.emplace("SRC", src->getDebugData());
110 }
Matt Spinlerc63e2e82019-12-02 15:50:12 -0600111
Matt Spinler4bfc9082020-03-24 15:05:54 -0500112 auto euh = std::make_unique<ExtendedUserHeader>(dataIface, regEntry, *src);
Matt Spinlerc63e2e82019-12-02 15:50:12 -0600113
Matt Spinlerbd716f02019-10-15 10:54:11 -0500114 _optionalSections.push_back(std::move(src));
Matt Spinlerc63e2e82019-12-02 15:50:12 -0600115 _optionalSections.push_back(std::move(euh));
Matt Spinlerb8323632019-09-20 15:11:04 -0500116
Matt Spinleraa659472019-10-23 09:26:48 -0500117 auto mtms = std::make_unique<FailingMTMS>(dataIface);
118 _optionalSections.push_back(std::move(mtms));
Matt Spinlerbd716f02019-10-15 10:54:11 -0500119
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600120 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface);
Matt Spinler85f61a62020-06-03 16:28:55 -0500121 addUserDataSection(std::move(ud));
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600122
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500123 // Create a UserData section from AdditionalData.
Matt Spinlerafa857c2019-10-24 13:03:46 -0500124 if (!additionalData.empty())
125 {
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600126 ud = util::makeADUserDataSection(additionalData);
Matt Spinler85f61a62020-06-03 16:28:55 -0500127 addUserDataSection(std::move(ud));
Matt Spinlerafa857c2019-10-24 13:03:46 -0500128 }
129
Matt Spinler56ad2a02020-03-26 14:00:52 -0500130 // Add any FFDC files into UserData sections
131 for (const auto& file : ffdcFiles)
132 {
133 ud = util::makeFFDCuserDataSection(regEntry.componentID, file);
134 if (!ud)
135 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500136 // Add this error into the debug data UserData section
137 std::ostringstream msg;
138 msg << "Could not make PEL FFDC UserData section from file"
139 << std::hex << regEntry.componentID << " " << file.subType
140 << " " << file.version;
141 if (debugData.count("FFDC File"))
142 {
143 debugData.at("FFDC File").push_back(msg.str());
144 }
145 else
146 {
147 debugData.emplace("FFDC File",
148 std::vector<std::string>{msg.str()});
149 }
150
Matt Spinler56ad2a02020-03-26 14:00:52 -0500151 continue;
152 }
153
Matt Spinler85f61a62020-06-03 16:28:55 -0500154 addUserDataSection(std::move(ud));
155 }
Matt Spinler56ad2a02020-03-26 14:00:52 -0500156
Matt Spinler85f61a62020-06-03 16:28:55 -0500157 // Store in the PEL any important debug data created while
158 // building the PEL sections.
159 if (!debugData.empty())
160 {
161 nlohmann::json data;
162 data["PEL Internal Debug Data"] = debugData;
163 ud = util::makeJSONUserDataSection(data);
164
165 addUserDataSection(std::move(ud));
166
167 // Also put in the journal for debug
168 for (const auto& [name, data] : debugData)
169 {
170 for (const auto& message : data)
171 {
172 std::string entry = name + ": " + message;
173 log<level::INFO>(entry.c_str());
Matt Spinler56ad2a02020-03-26 14:00:52 -0500174 }
175 }
Matt Spinler56ad2a02020-03-26 14:00:52 -0500176 }
177
Matt Spinler97d19b42019-10-29 11:34:03 -0500178 _ph->setSectionCount(2 + _optionalSections.size());
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500179
180 checkRulesAndFix();
Matt Spinlerb8323632019-09-20 15:11:04 -0500181}
Matt Spinlercb6b0592019-07-16 15:58:51 -0500182
Matt Spinler07eefc52019-09-26 11:18:26 -0500183PEL::PEL(std::vector<uint8_t>& data) : PEL(data, 0)
Matt Spinlercb6b0592019-07-16 15:58:51 -0500184{
185}
186
Matt Spinler07eefc52019-09-26 11:18:26 -0500187PEL::PEL(std::vector<uint8_t>& data, uint32_t obmcLogID)
Matt Spinlercb6b0592019-07-16 15:58:51 -0500188{
Matt Spinler07eefc52019-09-26 11:18:26 -0500189 populateFromRawData(data, obmcLogID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500190}
191
Matt Spinler07eefc52019-09-26 11:18:26 -0500192void PEL::populateFromRawData(std::vector<uint8_t>& data, uint32_t obmcLogID)
Matt Spinlercb6b0592019-07-16 15:58:51 -0500193{
Matt Spinler07eefc52019-09-26 11:18:26 -0500194 Stream pelData{data};
Matt Spinlercb6b0592019-07-16 15:58:51 -0500195 _ph = std::make_unique<PrivateHeader>(pelData);
196 if (obmcLogID != 0)
197 {
Matt Spinler97d19b42019-10-29 11:34:03 -0500198 _ph->setOBMCLogID(obmcLogID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500199 }
200
201 _uh = std::make_unique<UserHeader>(pelData);
Matt Spinler131870c2019-09-25 13:29:04 -0500202
203 // Use the section factory to create the rest of the objects
204 for (size_t i = 2; i < _ph->sectionCount(); i++)
205 {
206 auto section = section_factory::create(pelData);
207 _optionalSections.push_back(std::move(section));
208 }
Matt Spinlercb6b0592019-07-16 15:58:51 -0500209}
210
211bool PEL::valid() const
212{
213 bool valid = _ph->valid();
214
215 if (valid)
216 {
217 valid = _uh->valid();
218 }
219
Matt Spinler131870c2019-09-25 13:29:04 -0500220 if (valid)
221 {
222 if (!std::all_of(_optionalSections.begin(), _optionalSections.end(),
223 [](const auto& section) { return section->valid(); }))
224 {
225 valid = false;
226 }
227 }
228
Matt Spinlercb6b0592019-07-16 15:58:51 -0500229 return valid;
230}
231
232void PEL::setCommitTime()
233{
234 auto now = std::chrono::system_clock::now();
Matt Spinler97d19b42019-10-29 11:34:03 -0500235 _ph->setCommitTimestamp(getBCDTime(now));
Matt Spinlercb6b0592019-07-16 15:58:51 -0500236}
237
238void PEL::assignID()
239{
Matt Spinler97d19b42019-10-29 11:34:03 -0500240 _ph->setID(generatePELID());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500241}
242
Matt Spinler06885452019-11-06 10:35:42 -0600243void PEL::flatten(std::vector<uint8_t>& pelBuffer) const
Matt Spinlercb6b0592019-07-16 15:58:51 -0500244{
245 Stream pelData{pelBuffer};
Matt Spinlerb8323632019-09-20 15:11:04 -0500246
Matt Spinler07eefc52019-09-26 11:18:26 -0500247 if (!valid())
Matt Spinlercb6b0592019-07-16 15:58:51 -0500248 {
Matt Spinler07eefc52019-09-26 11:18:26 -0500249 log<level::WARNING>("Unflattening an invalid PEL");
Matt Spinlercb6b0592019-07-16 15:58:51 -0500250 }
251
Matt Spinler07eefc52019-09-26 11:18:26 -0500252 _ph->flatten(pelData);
Matt Spinlerb8323632019-09-20 15:11:04 -0500253 _uh->flatten(pelData);
Matt Spinler07eefc52019-09-26 11:18:26 -0500254
255 for (auto& section : _optionalSections)
256 {
257 section->flatten(pelData);
258 }
Matt Spinlercb6b0592019-07-16 15:58:51 -0500259}
260
Matt Spinler06885452019-11-06 10:35:42 -0600261std::vector<uint8_t> PEL::data() const
Matt Spinlercb6b0592019-07-16 15:58:51 -0500262{
Matt Spinler07eefc52019-09-26 11:18:26 -0500263 std::vector<uint8_t> pelData;
264 flatten(pelData);
265 return pelData;
Matt Spinlercb6b0592019-07-16 15:58:51 -0500266}
267
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600268size_t PEL::size() const
269{
270 size_t size = 0;
271
272 if (_ph)
273 {
274 size += _ph->header().size;
275 }
276
277 if (_uh)
278 {
279 size += _uh->header().size;
280 }
281
282 for (const auto& section : _optionalSections)
283 {
284 size += section->header().size;
285 }
286
287 return size;
288}
289
Matt Spinlerbd716f02019-10-15 10:54:11 -0500290std::optional<SRC*> PEL::primarySRC() const
291{
292 auto src = std::find_if(
293 _optionalSections.begin(), _optionalSections.end(), [](auto& section) {
294 return section->header().id ==
295 static_cast<uint16_t>(SectionID::primarySRC);
296 });
297 if (src != _optionalSections.end())
298 {
299 return static_cast<SRC*>(src->get());
300 }
301
302 return std::nullopt;
303}
304
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500305void PEL::checkRulesAndFix()
306{
Matt Spinler1f93c592020-09-10 10:43:08 -0500307 // Only fix if the action flags are at their default value which
308 // means they weren't specified in the registry. Otherwise
309 // assume the user knows what they are doing.
310 if (_uh->actionFlags() == actionFlagsDefault)
311 {
312 auto [actionFlags, eventType] =
313 pel_rules::check(0, _uh->eventType(), _uh->severity());
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500314
Matt Spinler1f93c592020-09-10 10:43:08 -0500315 _uh->setActionFlags(actionFlags);
316 _uh->setEventType(eventType);
317 }
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500318}
319
Matt Spinleracb7c102020-01-10 13:49:22 -0600320void PEL::printSectionInJSON(const Section& section, std::string& buf,
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800321 std::map<uint16_t, size_t>& pluralSections,
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800322 message::Registry& registry,
323 const std::vector<std::string>& plugins,
324 uint8_t creatorID) const
Aatir186ce8c2019-10-20 15:13:39 -0500325{
326 char tmpB[5];
Aatir Manzurad0e0472019-10-07 13:18:37 -0500327 uint8_t id[] = {static_cast<uint8_t>(section.header().id >> 8),
328 static_cast<uint8_t>(section.header().id)};
329 sprintf(tmpB, "%c%c", id[0], id[1]);
330 std::string sectionID(tmpB);
331 std::string sectionName = pv::sectionTitles.count(sectionID)
332 ? pv::sectionTitles.at(sectionID)
333 : "Unknown Section";
Matt Spinleracb7c102020-01-10 13:49:22 -0600334
335 // Add a count if there are multiple of this type of section
336 auto count = pluralSections.find(section.header().id);
337 if (count != pluralSections.end())
338 {
339 sectionName += " " + std::to_string(count->second);
340 count->second++;
341 }
342
Aatir186ce8c2019-10-20 15:13:39 -0500343 if (section.valid())
344 {
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800345 std::optional<std::string> json;
346 if (sectionID == "PS" || sectionID == "SS")
347 {
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800348 json = section.getJSON(registry, plugins, creatorID);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800349 }
Matt Spinler386a61e2020-08-13 15:51:12 -0500350 else if ((sectionID == "UD") || (sectionID == "ED"))
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800351 {
Harisuddin Mohamed Isa3fdcd4e2020-08-26 11:56:42 +0800352 json = section.getJSON(creatorID, plugins);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800353 }
354 else
355 {
356 json = section.getJSON();
357 }
Matt Spinler4220a152020-03-26 10:18:09 -0500358
359 buf += "\"" + sectionName + "\": {\n";
360
Aatir Manzurad0e0472019-10-07 13:18:37 -0500361 if (json)
362 {
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800363 buf += *json + "\n},\n";
Aatir Manzurad0e0472019-10-07 13:18:37 -0500364 }
365 else
366 {
Matt Spinler4220a152020-03-26 10:18:09 -0500367 jsonInsert(buf, pv::sectionVer,
368 getNumberString("%d", section.header().version), 1);
369 jsonInsert(buf, pv::subSection,
370 getNumberString("%d", section.header().subType), 1);
371 jsonInsert(buf, pv::createdBy,
372 getNumberString("0x%X", section.header().componentID),
373 1);
374
Aatir Manzurad0e0472019-10-07 13:18:37 -0500375 std::vector<uint8_t> data;
376 Stream s{data};
377 section.flatten(s);
Matt Spinler4220a152020-03-26 10:18:09 -0500378 std::string dstr =
379 dumpHex(std::data(data) + SectionHeader::flattenedSize(),
Harisuddin Mohamed Isa097ad122020-06-11 21:19:41 +0800380 data.size() - SectionHeader::flattenedSize(), 2);
Matt Spinler4220a152020-03-26 10:18:09 -0500381 std::string jsonIndent(indentLevel, 0x20);
382 buf += jsonIndent + "\"Data\": [\n";
383 buf += dstr;
384 buf += jsonIndent + "]\n";
385 buf += "},\n";
Aatir Manzurad0e0472019-10-07 13:18:37 -0500386 }
Aatir186ce8c2019-10-20 15:13:39 -0500387 }
388 else
389 {
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800390 buf += "\n\"Invalid Section\": [\n \"invalid\"\n],\n";
Aatir186ce8c2019-10-20 15:13:39 -0500391 }
392}
393
Matt Spinleracb7c102020-01-10 13:49:22 -0600394std::map<uint16_t, size_t> PEL::getPluralSections() const
395{
396 std::map<uint16_t, size_t> sectionCounts;
397
398 for (const auto& section : optionalSections())
399 {
400 if (sectionCounts.find(section->header().id) == sectionCounts.end())
401 {
402 sectionCounts[section->header().id] = 1;
403 }
404 else
405 {
406 sectionCounts[section->header().id]++;
407 }
408 }
409
410 std::map<uint16_t, size_t> sections;
411 for (const auto& [id, count] : sectionCounts)
412 {
413 if (count > 1)
414 {
415 // Start with 0 here and printSectionInJSON()
416 // will increment it as it goes.
417 sections.emplace(id, 0);
418 }
419 }
420
421 return sections;
422}
423
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800424void PEL::toJSON(message::Registry& registry,
425 const std::vector<std::string>& plugins) const
Aatir186ce8c2019-10-20 15:13:39 -0500426{
Matt Spinleracb7c102020-01-10 13:49:22 -0600427 auto sections = getPluralSections();
428
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800429 std::string buf = "{\n";
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800430 printSectionInJSON(*(_ph.get()), buf, sections, registry, plugins);
431 printSectionInJSON(*(_uh.get()), buf, sections, registry, plugins);
Aatir186ce8c2019-10-20 15:13:39 -0500432 for (auto& section : this->optionalSections())
433 {
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800434 printSectionInJSON(*(section.get()), buf, sections, registry, plugins,
435 _ph->creatorID());
Aatir186ce8c2019-10-20 15:13:39 -0500436 }
437 buf += "}";
438 std::size_t found = buf.rfind(",");
439 if (found != std::string::npos)
440 buf.replace(found, 1, "");
441 std::cout << buf << std::endl;
442}
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800443
Matt Spinler85f61a62020-06-03 16:28:55 -0500444bool PEL::addUserDataSection(std::unique_ptr<UserData> userData)
445{
446 if (size() + userData->header().size > _maxPELSize)
447 {
448 if (userData->shrink(_maxPELSize - size()))
449 {
450 _optionalSections.push_back(std::move(userData));
451 }
452 else
453 {
454 log<level::WARNING>(
455 "Could not shrink UserData section. Dropping",
456 entry("SECTION_SIZE=%d\n", userData->header().size),
457 entry("COMPONENT_ID=0x%02X", userData->header().componentID),
458 entry("SUBTYPE=0x%X", userData->header().subType),
459 entry("VERSION=0x%X", userData->header().version));
460 return false;
461 }
462 }
463 else
464 {
465 _optionalSections.push_back(std::move(userData));
466 }
467 return true;
468}
469
Matt Spinler5a90a952020-08-27 09:39:03 -0500470nlohmann::json PEL::getCalloutJSON(const PelFFDC& ffdcFiles)
471{
472 nlohmann::json callouts;
473
474 for (const auto& file : ffdcFiles)
475 {
476 if ((file.format == UserDataFormat::json) &&
477 (file.subType == jsonCalloutSubtype))
478 {
479 auto data = util::readFD(file.fd);
480 if (data.empty())
481 {
482 throw std::runtime_error{
483 "Could not get data from JSON callout file descriptor"};
484 }
485
486 std::string jsonString{data.begin(), data.begin() + data.size()};
487
488 callouts = nlohmann::json::parse(jsonString);
489 break;
490 }
491 }
492
493 return callouts;
494}
495
Andrew Geissler44fc3162020-07-09 09:21:31 -0500496bool PEL::isCalloutPresent() const
497{
498 auto pSRC = primarySRC();
499 if (!pSRC)
500 {
501 return false;
502 }
503
504 bool calloutPresent = false;
505 if ((*pSRC)->callouts())
506 {
507 for (auto& i : (*pSRC)->callouts()->callouts())
508 {
509 if (((*i).fruIdentity()))
510 {
511 auto& fruId = (*i).fruIdentity();
512 if ((*fruId).failingComponentType() != 0)
513 {
514 calloutPresent = true;
515 break;
516 }
517 }
518 }
519 }
520
521 return calloutPresent;
522}
523
Sumit Kumar3160a542021-04-26 08:07:04 -0500524void PEL::updateSysInfoInExtendedUserDataSection(
525 const DataInterfaceBase& dataIface)
526{
527 const AdditionalData additionalData;
528
529 // Check for PEL from Hostboot
530 if (_ph->creatorID() == static_cast<uint8_t>(CreatorID::hostboot))
531 {
532 // Get the ED section from PEL
533 auto op = std::find_if(_optionalSections.begin(),
534 _optionalSections.end(), [](auto& section) {
535 return section->header().id ==
536 static_cast<uint16_t>(
537 SectionID::extUserData);
538 });
539
540 // Check for ED section found and its not the last section of PEL
541 if (op != _optionalSections.end())
542 {
543 // Get the extended user data class mapped to found section
544 auto extUserData = static_cast<ExtendedUserData*>(op->get());
545
546 // Check for the creator ID is for OpenBMC
547 if (extUserData->creatorID() ==
548 static_cast<uint8_t>(CreatorID::openBMC))
549 {
550 // Update subtype and component id
551 auto subType = static_cast<uint8_t>(UserDataFormat::json);
552 auto componentId =
553 static_cast<uint16_t>(ComponentID::phosphorLogging);
554
555 // Update system data to ED section
556 auto ud =
557 util::makeSysInfoUserDataSection(additionalData, dataIface);
558 extUserData->updateDataSection(subType, componentId,
559 ud->data());
560 }
561 }
562 }
563}
564
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600565namespace util
566{
567
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600568std::unique_ptr<UserData> makeJSONUserDataSection(const nlohmann::json& json)
569{
570 auto jsonString = json.dump();
571 std::vector<uint8_t> jsonData(jsonString.begin(), jsonString.end());
572
573 // Pad to a 4 byte boundary
574 while ((jsonData.size() % 4) != 0)
575 {
576 jsonData.push_back(0);
577 }
578
579 return std::make_unique<UserData>(
580 static_cast<uint16_t>(ComponentID::phosphorLogging),
581 static_cast<uint8_t>(UserDataFormat::json),
582 static_cast<uint8_t>(UserDataFormatVersion::json), jsonData);
583}
584
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600585std::unique_ptr<UserData> makeADUserDataSection(const AdditionalData& ad)
586{
587 assert(!ad.empty());
588 nlohmann::json json;
589
590 // Remove the 'ESEL' entry, as it contains a full PEL in the value.
591 if (ad.getValue("ESEL"))
592 {
593 auto newAD = ad;
594 newAD.remove("ESEL");
595 json = newAD.toJSON();
596 }
597 else
598 {
599 json = ad.toJSON();
600 }
601
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600602 return makeJSONUserDataSection(json);
603}
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600604
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600605void addProcessNameToJSON(nlohmann::json& json,
606 const std::optional<std::string>& pid,
607 const DataInterfaceBase& dataIface)
608{
Matt Spinler677381b2020-01-23 10:04:29 -0600609 std::string name{unknownValue};
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600610
611 try
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600612 {
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600613 if (pid)
614 {
615 auto n = dataIface.getProcessName(*pid);
616 if (n)
617 {
618 name = *n;
619 }
620 }
621 }
622 catch (std::exception& e)
623 {
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600624 }
625
Sumit Kumar3160a542021-04-26 08:07:04 -0500626 if (pid)
627 {
628 json["Process Name"] = std::move(name);
629 }
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600630}
631
Matt Spinler677381b2020-01-23 10:04:29 -0600632void addBMCFWVersionIDToJSON(nlohmann::json& json,
633 const DataInterfaceBase& dataIface)
634{
635 auto id = dataIface.getBMCFWVersionID();
636 if (id.empty())
637 {
638 id = unknownValue;
639 }
640
Matt Spinlerc2b8a512021-05-21 12:44:42 -0600641 json["FW Version ID"] = std::move(id);
Matt Spinler677381b2020-01-23 10:04:29 -0600642}
643
Matt Spinler4aa23a12020-02-03 15:05:09 -0600644std::string lastSegment(char separator, std::string data)
645{
646 auto pos = data.find_last_of(separator);
647 if (pos != std::string::npos)
648 {
649 data = data.substr(pos + 1);
650 }
651
652 return data;
653}
654
Ben Tynere32b7e72021-05-18 12:38:40 -0500655void addIMKeyword(nlohmann::json& json, const DataInterfaceBase& dataIface)
656{
657 auto keyword = dataIface.getSystemIMKeyword();
658
659 std::string value{};
660
661 std::for_each(keyword.begin(), keyword.end(), [&](const auto& byte) {
662 value += fmt::format("{:02X}", byte);
663 });
664
665 json["System IM"] = value;
666}
667
Matt Spinler4aa23a12020-02-03 15:05:09 -0600668void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface)
669{
670 json["BMCState"] = lastSegment('.', dataIface.getBMCState());
671 json["ChassisState"] = lastSegment('.', dataIface.getChassisState());
672 json["HostState"] = lastSegment('.', dataIface.getHostState());
673}
674
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600675std::unique_ptr<UserData>
676 makeSysInfoUserDataSection(const AdditionalData& ad,
677 const DataInterfaceBase& dataIface)
678{
679 nlohmann::json json;
680
681 addProcessNameToJSON(json, ad.getValue("_PID"), dataIface);
Matt Spinler677381b2020-01-23 10:04:29 -0600682 addBMCFWVersionIDToJSON(json, dataIface);
Ben Tynere32b7e72021-05-18 12:38:40 -0500683 addIMKeyword(json, dataIface);
Matt Spinler4aa23a12020-02-03 15:05:09 -0600684 addStatesToJSON(json, dataIface);
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600685
686 return makeJSONUserDataSection(json);
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600687}
688
Matt Spinler5b289b22020-03-26 14:27:19 -0500689std::vector<uint8_t> readFD(int fd)
690{
691 std::vector<uint8_t> data;
692
693 // Get the size
694 struct stat s;
695 int r = fstat(fd, &s);
696 if (r != 0)
697 {
698 auto e = errno;
699 log<level::ERR>("Could not get FFDC file size from FD",
700 entry("ERRNO=%d", e));
701 return data;
702 }
703
704 if (0 == s.st_size)
705 {
706 log<level::ERR>("FFDC file is empty");
707 return data;
708 }
709
710 data.resize(s.st_size);
711
712 // Make sure its at the beginning, as maybe another
713 // extension already used it.
714 r = lseek(fd, 0, SEEK_SET);
715 if (r == -1)
716 {
717 auto e = errno;
718 log<level::ERR>("Could not seek to beginning of FFDC file",
719 entry("ERRNO=%d", e));
720 return data;
721 }
722
723 r = read(fd, data.data(), s.st_size);
724 if (r == -1)
725 {
726 auto e = errno;
727 log<level::ERR>("Could not read FFDC file", entry("ERRNO=%d", e));
728 }
729 else if (r != s.st_size)
730 {
731 log<level::WARNING>("Could not read full FFDC file",
732 entry("FILE_SIZE=%d", s.st_size),
733 entry("SIZE_READ=%d", r));
734 }
735
736 return data;
737}
738
Matt Spinler56ad2a02020-03-26 14:00:52 -0500739std::unique_ptr<UserData> makeFFDCuserDataSection(uint16_t componentID,
740 const PelFFDCfile& file)
741{
Matt Spinler5b289b22020-03-26 14:27:19 -0500742 auto data = readFD(file.fd);
743
744 if (data.empty())
745 {
746 return std::unique_ptr<UserData>();
747 }
748
749 // The data needs 4 Byte alignment, and save amount padded for the
750 // CBOR case.
751 uint32_t pad = 0;
752 while (data.size() % 4)
753 {
754 data.push_back(0);
755 pad++;
756 }
757
758 // For JSON, CBOR, and Text use our component ID, subType, and version,
759 // otherwise use the supplied ones.
760 uint16_t compID = static_cast<uint16_t>(ComponentID::phosphorLogging);
761 uint8_t subType{};
762 uint8_t version{};
763
764 switch (file.format)
765 {
766 case UserDataFormat::json:
767 subType = static_cast<uint8_t>(UserDataFormat::json);
768 version = static_cast<uint8_t>(UserDataFormatVersion::json);
769 break;
770 case UserDataFormat::cbor:
771 subType = static_cast<uint8_t>(UserDataFormat::cbor);
772 version = static_cast<uint8_t>(UserDataFormatVersion::cbor);
773
774 // The CBOR parser will fail on the extra pad bytes since they
775 // aren't CBOR. Add the amount we padded to the end and other
776 // code will remove it all before parsing.
777 {
778 data.resize(data.size() + 4);
779 Stream stream{data};
780 stream.offset(data.size() - 4);
781 stream << pad;
782 }
783
784 break;
785 case UserDataFormat::text:
786 subType = static_cast<uint8_t>(UserDataFormat::text);
787 version = static_cast<uint8_t>(UserDataFormatVersion::text);
788 break;
789 case UserDataFormat::custom:
790 default:
791 // Use the passed in values
792 compID = componentID;
793 subType = file.subType;
794 version = file.version;
795 break;
796 }
797
798 return std::make_unique<UserData>(compID, subType, version, data);
Matt Spinler56ad2a02020-03-26 14:00:52 -0500799}
800
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600801} // namespace util
802
Matt Spinlercb6b0592019-07-16 15:58:51 -0500803} // namespace pels
804} // namespace openpower