blob: d6c404d163ce0d0542c46c06253ed4cd32598fb3 [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 Spinlerc63e2e82019-12-02 15:50:12 -060019#include "extended_user_header.hpp"
Matt Spinleraa659472019-10-23 09:26:48 -050020#include "failing_mtms.hpp"
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +080021#include "json_utils.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050022#include "log_id.hpp"
Matt Spinlerf1e85e22019-11-01 11:31:31 -050023#include "pel_rules.hpp"
Aatir186ce8c2019-10-20 15:13:39 -050024#include "pel_values.hpp"
Matt Spinler131870c2019-09-25 13:29:04 -050025#include "section_factory.hpp"
Matt Spinlerbd716f02019-10-15 10:54:11 -050026#include "src.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050027#include "stream.hpp"
Matt Spinlerafa857c2019-10-24 13:03:46 -050028#include "user_data_formats.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050029
Aatir186ce8c2019-10-20 15:13:39 -050030#include <iostream>
Matt Spinler07eefc52019-09-26 11:18:26 -050031#include <phosphor-logging/log.hpp>
32
Matt Spinlercb6b0592019-07-16 15:58:51 -050033namespace openpower
34{
35namespace pels
36{
Matt Spinlerb8323632019-09-20 15:11:04 -050037namespace message = openpower::pels::message;
Aatir186ce8c2019-10-20 15:13:39 -050038namespace pv = openpower::pels::pel_values;
Matt Spinler4bfc9082020-03-24 15:05:54 -050039using namespace phosphor::logging;
Matt Spinlerb8323632019-09-20 15:11:04 -050040
Matt Spinler677381b2020-01-23 10:04:29 -060041constexpr auto unknownValue = "Unknown";
42
Matt Spinler4bfc9082020-03-24 15:05:54 -050043PEL::PEL(const message::Entry& regEntry, uint32_t obmcLogID, uint64_t timestamp,
Matt Spinlerbd716f02019-10-15 10:54:11 -050044 phosphor::logging::Entry::Level severity,
Matt Spinler56ad2a02020-03-26 14:00:52 -050045 const AdditionalData& additionalData, const PelFFDC& ffdcFiles,
Matt Spinleraa659472019-10-23 09:26:48 -050046 const DataInterfaceBase& dataIface)
Matt Spinlerb8323632019-09-20 15:11:04 -050047{
Matt Spinler4bfc9082020-03-24 15:05:54 -050048 _ph = std::make_unique<PrivateHeader>(regEntry.componentID, obmcLogID,
Matt Spinlerb8323632019-09-20 15:11:04 -050049 timestamp);
Matt Spinler4bfc9082020-03-24 15:05:54 -050050 _uh = std::make_unique<UserHeader>(regEntry, severity);
Matt Spinlerb8323632019-09-20 15:11:04 -050051
Matt Spinler4bfc9082020-03-24 15:05:54 -050052 auto src = std::make_unique<SRC>(regEntry, additionalData, dataIface);
Matt Spinlerc63e2e82019-12-02 15:50:12 -060053
Matt Spinler4bfc9082020-03-24 15:05:54 -050054 auto euh = std::make_unique<ExtendedUserHeader>(dataIface, regEntry, *src);
Matt Spinlerc63e2e82019-12-02 15:50:12 -060055
Matt Spinlerbd716f02019-10-15 10:54:11 -050056 _optionalSections.push_back(std::move(src));
Matt Spinlerc63e2e82019-12-02 15:50:12 -060057 _optionalSections.push_back(std::move(euh));
Matt Spinlerb8323632019-09-20 15:11:04 -050058
Matt Spinleraa659472019-10-23 09:26:48 -050059 auto mtms = std::make_unique<FailingMTMS>(dataIface);
60 _optionalSections.push_back(std::move(mtms));
Matt Spinlerbd716f02019-10-15 10:54:11 -050061
Matt Spinler4dcd3f42020-01-22 14:55:07 -060062 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface);
63 _optionalSections.push_back(std::move(ud));
64
Matt Spinler9b7e94f2020-03-24 15:44:41 -050065 // Create a UserData section from AdditionalData.
Matt Spinlerafa857c2019-10-24 13:03:46 -050066 if (!additionalData.empty())
67 {
Matt Spinler4dcd3f42020-01-22 14:55:07 -060068 ud = util::makeADUserDataSection(additionalData);
Matt Spinler6d663822020-01-22 14:50:46 -060069
Matt Spinler9b7e94f2020-03-24 15:44:41 -050070 // Shrink the section if necessary.
71 if (size() + ud->header().size > _maxPELSize)
72 {
73 if (ud->shrink(_maxPELSize - size()))
74 {
75 _optionalSections.push_back(std::move(ud));
76 }
77 else
78 {
79 log<level::WARNING>(
80 "Dropping AdditionalData UserData section",
81 entry("SECTION_SIZE=%d\n", ud->header().size));
82 }
83 }
84 else
Matt Spinler6d663822020-01-22 14:50:46 -060085 {
86 _optionalSections.push_back(std::move(ud));
87 }
Matt Spinlerafa857c2019-10-24 13:03:46 -050088 }
89
Matt Spinler56ad2a02020-03-26 14:00:52 -050090 // Add any FFDC files into UserData sections
91 for (const auto& file : ffdcFiles)
92 {
93 ud = util::makeFFDCuserDataSection(regEntry.componentID, file);
94 if (!ud)
95 {
96 log<level::WARNING>(
97 "Could not make PEL FFDC UserData section from file",
98 entry("COMPONENT_ID=0x%02X", regEntry.componentID),
99 entry("SUBTYPE=0x%X", file.subType),
100 entry("VERSION=0x%X", file.version));
101 continue;
102 }
103
104 // Shrink it if necessary
105 if (size() + ud->header().size > _maxPELSize)
106 {
107 if (!ud->shrink(_maxPELSize - size()))
108 {
109 log<level::WARNING>(
110 "Could not shrink FFDC UserData section",
111 entry("COMPONENT_ID=0x%02X", regEntry.componentID),
112 entry("SUBTYPE=0x%X", file.subType),
113 entry("VERSION=0x%X", file.version));
114
115 // Give up adding FFDC
116 break;
117 }
118 }
119
120 _optionalSections.push_back(std::move(ud));
121 }
122
Matt Spinler97d19b42019-10-29 11:34:03 -0500123 _ph->setSectionCount(2 + _optionalSections.size());
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500124
125 checkRulesAndFix();
Matt Spinlerb8323632019-09-20 15:11:04 -0500126}
Matt Spinlercb6b0592019-07-16 15:58:51 -0500127
Matt Spinler07eefc52019-09-26 11:18:26 -0500128PEL::PEL(std::vector<uint8_t>& data) : PEL(data, 0)
Matt Spinlercb6b0592019-07-16 15:58:51 -0500129{
130}
131
Matt Spinler07eefc52019-09-26 11:18:26 -0500132PEL::PEL(std::vector<uint8_t>& data, uint32_t obmcLogID)
Matt Spinlercb6b0592019-07-16 15:58:51 -0500133{
Matt Spinler07eefc52019-09-26 11:18:26 -0500134 populateFromRawData(data, obmcLogID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500135}
136
Matt Spinler07eefc52019-09-26 11:18:26 -0500137void PEL::populateFromRawData(std::vector<uint8_t>& data, uint32_t obmcLogID)
Matt Spinlercb6b0592019-07-16 15:58:51 -0500138{
Matt Spinler07eefc52019-09-26 11:18:26 -0500139 Stream pelData{data};
Matt Spinlercb6b0592019-07-16 15:58:51 -0500140 _ph = std::make_unique<PrivateHeader>(pelData);
141 if (obmcLogID != 0)
142 {
Matt Spinler97d19b42019-10-29 11:34:03 -0500143 _ph->setOBMCLogID(obmcLogID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500144 }
145
146 _uh = std::make_unique<UserHeader>(pelData);
Matt Spinler131870c2019-09-25 13:29:04 -0500147
148 // Use the section factory to create the rest of the objects
149 for (size_t i = 2; i < _ph->sectionCount(); i++)
150 {
151 auto section = section_factory::create(pelData);
152 _optionalSections.push_back(std::move(section));
153 }
Matt Spinlercb6b0592019-07-16 15:58:51 -0500154}
155
156bool PEL::valid() const
157{
158 bool valid = _ph->valid();
159
160 if (valid)
161 {
162 valid = _uh->valid();
163 }
164
Matt Spinler131870c2019-09-25 13:29:04 -0500165 if (valid)
166 {
167 if (!std::all_of(_optionalSections.begin(), _optionalSections.end(),
168 [](const auto& section) { return section->valid(); }))
169 {
170 valid = false;
171 }
172 }
173
Matt Spinlercb6b0592019-07-16 15:58:51 -0500174 return valid;
175}
176
177void PEL::setCommitTime()
178{
179 auto now = std::chrono::system_clock::now();
Matt Spinler97d19b42019-10-29 11:34:03 -0500180 _ph->setCommitTimestamp(getBCDTime(now));
Matt Spinlercb6b0592019-07-16 15:58:51 -0500181}
182
183void PEL::assignID()
184{
Matt Spinler97d19b42019-10-29 11:34:03 -0500185 _ph->setID(generatePELID());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500186}
187
Matt Spinler06885452019-11-06 10:35:42 -0600188void PEL::flatten(std::vector<uint8_t>& pelBuffer) const
Matt Spinlercb6b0592019-07-16 15:58:51 -0500189{
190 Stream pelData{pelBuffer};
Matt Spinlerb8323632019-09-20 15:11:04 -0500191
Matt Spinler07eefc52019-09-26 11:18:26 -0500192 if (!valid())
Matt Spinlercb6b0592019-07-16 15:58:51 -0500193 {
Matt Spinler07eefc52019-09-26 11:18:26 -0500194 log<level::WARNING>("Unflattening an invalid PEL");
Matt Spinlercb6b0592019-07-16 15:58:51 -0500195 }
196
Matt Spinler07eefc52019-09-26 11:18:26 -0500197 _ph->flatten(pelData);
Matt Spinlerb8323632019-09-20 15:11:04 -0500198 _uh->flatten(pelData);
Matt Spinler07eefc52019-09-26 11:18:26 -0500199
200 for (auto& section : _optionalSections)
201 {
202 section->flatten(pelData);
203 }
Matt Spinlercb6b0592019-07-16 15:58:51 -0500204}
205
Matt Spinler06885452019-11-06 10:35:42 -0600206std::vector<uint8_t> PEL::data() const
Matt Spinlercb6b0592019-07-16 15:58:51 -0500207{
Matt Spinler07eefc52019-09-26 11:18:26 -0500208 std::vector<uint8_t> pelData;
209 flatten(pelData);
210 return pelData;
Matt Spinlercb6b0592019-07-16 15:58:51 -0500211}
212
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600213size_t PEL::size() const
214{
215 size_t size = 0;
216
217 if (_ph)
218 {
219 size += _ph->header().size;
220 }
221
222 if (_uh)
223 {
224 size += _uh->header().size;
225 }
226
227 for (const auto& section : _optionalSections)
228 {
229 size += section->header().size;
230 }
231
232 return size;
233}
234
Matt Spinlerbd716f02019-10-15 10:54:11 -0500235std::optional<SRC*> PEL::primarySRC() const
236{
237 auto src = std::find_if(
238 _optionalSections.begin(), _optionalSections.end(), [](auto& section) {
239 return section->header().id ==
240 static_cast<uint16_t>(SectionID::primarySRC);
241 });
242 if (src != _optionalSections.end())
243 {
244 return static_cast<SRC*>(src->get());
245 }
246
247 return std::nullopt;
248}
249
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500250void PEL::checkRulesAndFix()
251{
252 auto [actionFlags, eventType] =
253 pel_rules::check(_uh->actionFlags(), _uh->eventType(), _uh->severity());
254
255 _uh->setActionFlags(actionFlags);
256 _uh->setEventType(eventType);
257}
258
Matt Spinleracb7c102020-01-10 13:49:22 -0600259void PEL::printSectionInJSON(const Section& section, std::string& buf,
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800260 std::map<uint16_t, size_t>& pluralSections,
261 message::Registry& registry) const
Aatir186ce8c2019-10-20 15:13:39 -0500262{
263 char tmpB[5];
Aatir Manzurad0e0472019-10-07 13:18:37 -0500264 uint8_t id[] = {static_cast<uint8_t>(section.header().id >> 8),
265 static_cast<uint8_t>(section.header().id)};
266 sprintf(tmpB, "%c%c", id[0], id[1]);
267 std::string sectionID(tmpB);
268 std::string sectionName = pv::sectionTitles.count(sectionID)
269 ? pv::sectionTitles.at(sectionID)
270 : "Unknown Section";
Matt Spinleracb7c102020-01-10 13:49:22 -0600271
272 // Add a count if there are multiple of this type of section
273 auto count = pluralSections.find(section.header().id);
274 if (count != pluralSections.end())
275 {
276 sectionName += " " + std::to_string(count->second);
277 count->second++;
278 }
279
Aatir186ce8c2019-10-20 15:13:39 -0500280 if (section.valid())
281 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800282 auto json = (sectionID == "PS" || sectionID == "SS")
283 ? section.getJSON(registry)
284 : section.getJSON();
Matt Spinler4220a152020-03-26 10:18:09 -0500285
286 buf += "\"" + sectionName + "\": {\n";
287
Aatir Manzurad0e0472019-10-07 13:18:37 -0500288 if (json)
289 {
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800290 buf += *json + "\n},\n";
Aatir Manzurad0e0472019-10-07 13:18:37 -0500291 }
292 else
293 {
Matt Spinler4220a152020-03-26 10:18:09 -0500294 jsonInsert(buf, pv::sectionVer,
295 getNumberString("%d", section.header().version), 1);
296 jsonInsert(buf, pv::subSection,
297 getNumberString("%d", section.header().subType), 1);
298 jsonInsert(buf, pv::createdBy,
299 getNumberString("0x%X", section.header().componentID),
300 1);
301
Aatir Manzurad0e0472019-10-07 13:18:37 -0500302 std::vector<uint8_t> data;
303 Stream s{data};
304 section.flatten(s);
Matt Spinler4220a152020-03-26 10:18:09 -0500305 std::string dstr =
306 dumpHex(std::data(data) + SectionHeader::flattenedSize(),
307 data.size(), 2);
308
309 std::string jsonIndent(indentLevel, 0x20);
310 buf += jsonIndent + "\"Data\": [\n";
311 buf += dstr;
312 buf += jsonIndent + "]\n";
313 buf += "},\n";
Aatir Manzurad0e0472019-10-07 13:18:37 -0500314 }
Aatir186ce8c2019-10-20 15:13:39 -0500315 }
316 else
317 {
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800318 buf += "\n\"Invalid Section\": [\n \"invalid\"\n],\n";
Aatir186ce8c2019-10-20 15:13:39 -0500319 }
320}
321
Matt Spinleracb7c102020-01-10 13:49:22 -0600322std::map<uint16_t, size_t> PEL::getPluralSections() const
323{
324 std::map<uint16_t, size_t> sectionCounts;
325
326 for (const auto& section : optionalSections())
327 {
328 if (sectionCounts.find(section->header().id) == sectionCounts.end())
329 {
330 sectionCounts[section->header().id] = 1;
331 }
332 else
333 {
334 sectionCounts[section->header().id]++;
335 }
336 }
337
338 std::map<uint16_t, size_t> sections;
339 for (const auto& [id, count] : sectionCounts)
340 {
341 if (count > 1)
342 {
343 // Start with 0 here and printSectionInJSON()
344 // will increment it as it goes.
345 sections.emplace(id, 0);
346 }
347 }
348
349 return sections;
350}
351
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800352void PEL::toJSON(message::Registry& registry) const
Aatir186ce8c2019-10-20 15:13:39 -0500353{
Matt Spinleracb7c102020-01-10 13:49:22 -0600354 auto sections = getPluralSections();
355
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800356 std::string buf = "{\n";
357 printSectionInJSON(*(_ph.get()), buf, sections, registry);
358 printSectionInJSON(*(_uh.get()), buf, sections, registry);
Aatir186ce8c2019-10-20 15:13:39 -0500359 for (auto& section : this->optionalSections())
360 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800361 printSectionInJSON(*(section.get()), buf, sections, registry);
Aatir186ce8c2019-10-20 15:13:39 -0500362 }
363 buf += "}";
364 std::size_t found = buf.rfind(",");
365 if (found != std::string::npos)
366 buf.replace(found, 1, "");
367 std::cout << buf << std::endl;
368}
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800369
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600370namespace util
371{
372
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600373std::unique_ptr<UserData> makeJSONUserDataSection(const nlohmann::json& json)
374{
375 auto jsonString = json.dump();
376 std::vector<uint8_t> jsonData(jsonString.begin(), jsonString.end());
377
378 // Pad to a 4 byte boundary
379 while ((jsonData.size() % 4) != 0)
380 {
381 jsonData.push_back(0);
382 }
383
384 return std::make_unique<UserData>(
385 static_cast<uint16_t>(ComponentID::phosphorLogging),
386 static_cast<uint8_t>(UserDataFormat::json),
387 static_cast<uint8_t>(UserDataFormatVersion::json), jsonData);
388}
389
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600390std::unique_ptr<UserData> makeADUserDataSection(const AdditionalData& ad)
391{
392 assert(!ad.empty());
393 nlohmann::json json;
394
395 // Remove the 'ESEL' entry, as it contains a full PEL in the value.
396 if (ad.getValue("ESEL"))
397 {
398 auto newAD = ad;
399 newAD.remove("ESEL");
400 json = newAD.toJSON();
401 }
402 else
403 {
404 json = ad.toJSON();
405 }
406
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600407 return makeJSONUserDataSection(json);
408}
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600409
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600410void addProcessNameToJSON(nlohmann::json& json,
411 const std::optional<std::string>& pid,
412 const DataInterfaceBase& dataIface)
413{
Matt Spinler677381b2020-01-23 10:04:29 -0600414 std::string name{unknownValue};
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600415
416 try
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600417 {
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600418 if (pid)
419 {
420 auto n = dataIface.getProcessName(*pid);
421 if (n)
422 {
423 name = *n;
424 }
425 }
426 }
427 catch (std::exception& e)
428 {
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600429 }
430
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600431 json["Process Name"] = std::move(name);
432}
433
Matt Spinler677381b2020-01-23 10:04:29 -0600434void addBMCFWVersionIDToJSON(nlohmann::json& json,
435 const DataInterfaceBase& dataIface)
436{
437 auto id = dataIface.getBMCFWVersionID();
438 if (id.empty())
439 {
440 id = unknownValue;
441 }
442
443 json["BMC Version ID"] = std::move(id);
444}
445
Matt Spinler4aa23a12020-02-03 15:05:09 -0600446std::string lastSegment(char separator, std::string data)
447{
448 auto pos = data.find_last_of(separator);
449 if (pos != std::string::npos)
450 {
451 data = data.substr(pos + 1);
452 }
453
454 return data;
455}
456
457void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface)
458{
459 json["BMCState"] = lastSegment('.', dataIface.getBMCState());
460 json["ChassisState"] = lastSegment('.', dataIface.getChassisState());
461 json["HostState"] = lastSegment('.', dataIface.getHostState());
462}
463
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600464std::unique_ptr<UserData>
465 makeSysInfoUserDataSection(const AdditionalData& ad,
466 const DataInterfaceBase& dataIface)
467{
468 nlohmann::json json;
469
470 addProcessNameToJSON(json, ad.getValue("_PID"), dataIface);
Matt Spinler677381b2020-01-23 10:04:29 -0600471 addBMCFWVersionIDToJSON(json, dataIface);
Matt Spinler4aa23a12020-02-03 15:05:09 -0600472 addStatesToJSON(json, dataIface);
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600473
474 return makeJSONUserDataSection(json);
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600475}
476
Matt Spinler56ad2a02020-03-26 14:00:52 -0500477std::unique_ptr<UserData> makeFFDCuserDataSection(uint16_t componentID,
478 const PelFFDCfile& file)
479{
480 return std::unique_ptr<UserData>();
481}
482
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600483} // namespace util
484
Matt Spinlercb6b0592019-07-16 15:58:51 -0500485} // namespace pels
486} // namespace openpower