blob: 51147617b3c61840fdaa2d918a5dc55d6f484364 [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 Spinlerb8323632019-09-20 15:11:04 -050039
Matt Spinler677381b2020-01-23 10:04:29 -060040constexpr auto unknownValue = "Unknown";
41
Matt Spinlerb8323632019-09-20 15:11:04 -050042PEL::PEL(const message::Entry& entry, uint32_t obmcLogID, uint64_t timestamp,
Matt Spinlerbd716f02019-10-15 10:54:11 -050043 phosphor::logging::Entry::Level severity,
Matt Spinleraa659472019-10-23 09:26:48 -050044 const AdditionalData& additionalData,
45 const DataInterfaceBase& dataIface)
Matt Spinlerb8323632019-09-20 15:11:04 -050046{
47 _ph = std::make_unique<PrivateHeader>(entry.componentID, obmcLogID,
48 timestamp);
49 _uh = std::make_unique<UserHeader>(entry, severity);
50
Matt Spinler075e5ba2020-02-21 15:46:00 -060051 auto src = std::make_unique<SRC>(entry, additionalData, dataIface);
Matt Spinlerc63e2e82019-12-02 15:50:12 -060052
53 auto euh = std::make_unique<ExtendedUserHeader>(dataIface, entry, *src);
54
Matt Spinlerbd716f02019-10-15 10:54:11 -050055 _optionalSections.push_back(std::move(src));
Matt Spinlerc63e2e82019-12-02 15:50:12 -060056 _optionalSections.push_back(std::move(euh));
Matt Spinlerb8323632019-09-20 15:11:04 -050057
Matt Spinleraa659472019-10-23 09:26:48 -050058 auto mtms = std::make_unique<FailingMTMS>(dataIface);
59 _optionalSections.push_back(std::move(mtms));
Matt Spinlerbd716f02019-10-15 10:54:11 -050060
Matt Spinler4dcd3f42020-01-22 14:55:07 -060061 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface);
62 _optionalSections.push_back(std::move(ud));
63
Matt Spinlerafa857c2019-10-24 13:03:46 -050064 if (!additionalData.empty())
65 {
Matt Spinler4dcd3f42020-01-22 14:55:07 -060066 ud = util::makeADUserDataSection(additionalData);
Matt Spinler6d663822020-01-22 14:50:46 -060067
68 // To be safe, check there isn't too much data
69 if (size() + ud->header().size <= _maxPELSize)
70 {
71 _optionalSections.push_back(std::move(ud));
72 }
Matt Spinlerafa857c2019-10-24 13:03:46 -050073 }
74
Matt Spinler97d19b42019-10-29 11:34:03 -050075 _ph->setSectionCount(2 + _optionalSections.size());
Matt Spinlerf1e85e22019-11-01 11:31:31 -050076
77 checkRulesAndFix();
Matt Spinlerb8323632019-09-20 15:11:04 -050078}
Matt Spinlercb6b0592019-07-16 15:58:51 -050079
Matt Spinler07eefc52019-09-26 11:18:26 -050080PEL::PEL(std::vector<uint8_t>& data) : PEL(data, 0)
Matt Spinlercb6b0592019-07-16 15:58:51 -050081{
82}
83
Matt Spinler07eefc52019-09-26 11:18:26 -050084PEL::PEL(std::vector<uint8_t>& data, uint32_t obmcLogID)
Matt Spinlercb6b0592019-07-16 15:58:51 -050085{
Matt Spinler07eefc52019-09-26 11:18:26 -050086 populateFromRawData(data, obmcLogID);
Matt Spinlercb6b0592019-07-16 15:58:51 -050087}
88
Matt Spinler07eefc52019-09-26 11:18:26 -050089void PEL::populateFromRawData(std::vector<uint8_t>& data, uint32_t obmcLogID)
Matt Spinlercb6b0592019-07-16 15:58:51 -050090{
Matt Spinler07eefc52019-09-26 11:18:26 -050091 Stream pelData{data};
Matt Spinlercb6b0592019-07-16 15:58:51 -050092 _ph = std::make_unique<PrivateHeader>(pelData);
93 if (obmcLogID != 0)
94 {
Matt Spinler97d19b42019-10-29 11:34:03 -050095 _ph->setOBMCLogID(obmcLogID);
Matt Spinlercb6b0592019-07-16 15:58:51 -050096 }
97
98 _uh = std::make_unique<UserHeader>(pelData);
Matt Spinler131870c2019-09-25 13:29:04 -050099
100 // Use the section factory to create the rest of the objects
101 for (size_t i = 2; i < _ph->sectionCount(); i++)
102 {
103 auto section = section_factory::create(pelData);
104 _optionalSections.push_back(std::move(section));
105 }
Matt Spinlercb6b0592019-07-16 15:58:51 -0500106}
107
108bool PEL::valid() const
109{
110 bool valid = _ph->valid();
111
112 if (valid)
113 {
114 valid = _uh->valid();
115 }
116
Matt Spinler131870c2019-09-25 13:29:04 -0500117 if (valid)
118 {
119 if (!std::all_of(_optionalSections.begin(), _optionalSections.end(),
120 [](const auto& section) { return section->valid(); }))
121 {
122 valid = false;
123 }
124 }
125
Matt Spinlercb6b0592019-07-16 15:58:51 -0500126 return valid;
127}
128
129void PEL::setCommitTime()
130{
131 auto now = std::chrono::system_clock::now();
Matt Spinler97d19b42019-10-29 11:34:03 -0500132 _ph->setCommitTimestamp(getBCDTime(now));
Matt Spinlercb6b0592019-07-16 15:58:51 -0500133}
134
135void PEL::assignID()
136{
Matt Spinler97d19b42019-10-29 11:34:03 -0500137 _ph->setID(generatePELID());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500138}
139
Matt Spinler06885452019-11-06 10:35:42 -0600140void PEL::flatten(std::vector<uint8_t>& pelBuffer) const
Matt Spinlercb6b0592019-07-16 15:58:51 -0500141{
142 Stream pelData{pelBuffer};
Matt Spinlerb8323632019-09-20 15:11:04 -0500143
Matt Spinler07eefc52019-09-26 11:18:26 -0500144 if (!valid())
Matt Spinlercb6b0592019-07-16 15:58:51 -0500145 {
Matt Spinler07eefc52019-09-26 11:18:26 -0500146 using namespace phosphor::logging;
147 log<level::WARNING>("Unflattening an invalid PEL");
Matt Spinlercb6b0592019-07-16 15:58:51 -0500148 }
149
Matt Spinler07eefc52019-09-26 11:18:26 -0500150 _ph->flatten(pelData);
Matt Spinlerb8323632019-09-20 15:11:04 -0500151 _uh->flatten(pelData);
Matt Spinler07eefc52019-09-26 11:18:26 -0500152
153 for (auto& section : _optionalSections)
154 {
155 section->flatten(pelData);
156 }
Matt Spinlercb6b0592019-07-16 15:58:51 -0500157}
158
Matt Spinler06885452019-11-06 10:35:42 -0600159std::vector<uint8_t> PEL::data() const
Matt Spinlercb6b0592019-07-16 15:58:51 -0500160{
Matt Spinler07eefc52019-09-26 11:18:26 -0500161 std::vector<uint8_t> pelData;
162 flatten(pelData);
163 return pelData;
Matt Spinlercb6b0592019-07-16 15:58:51 -0500164}
165
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600166size_t PEL::size() const
167{
168 size_t size = 0;
169
170 if (_ph)
171 {
172 size += _ph->header().size;
173 }
174
175 if (_uh)
176 {
177 size += _uh->header().size;
178 }
179
180 for (const auto& section : _optionalSections)
181 {
182 size += section->header().size;
183 }
184
185 return size;
186}
187
Matt Spinlerbd716f02019-10-15 10:54:11 -0500188std::optional<SRC*> PEL::primarySRC() const
189{
190 auto src = std::find_if(
191 _optionalSections.begin(), _optionalSections.end(), [](auto& section) {
192 return section->header().id ==
193 static_cast<uint16_t>(SectionID::primarySRC);
194 });
195 if (src != _optionalSections.end())
196 {
197 return static_cast<SRC*>(src->get());
198 }
199
200 return std::nullopt;
201}
202
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500203void PEL::checkRulesAndFix()
204{
205 auto [actionFlags, eventType] =
206 pel_rules::check(_uh->actionFlags(), _uh->eventType(), _uh->severity());
207
208 _uh->setActionFlags(actionFlags);
209 _uh->setEventType(eventType);
210}
211
Matt Spinleracb7c102020-01-10 13:49:22 -0600212void PEL::printSectionInJSON(const Section& section, std::string& buf,
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800213 std::map<uint16_t, size_t>& pluralSections,
214 message::Registry& registry) const
Aatir186ce8c2019-10-20 15:13:39 -0500215{
216 char tmpB[5];
Aatir Manzurad0e0472019-10-07 13:18:37 -0500217 uint8_t id[] = {static_cast<uint8_t>(section.header().id >> 8),
218 static_cast<uint8_t>(section.header().id)};
219 sprintf(tmpB, "%c%c", id[0], id[1]);
220 std::string sectionID(tmpB);
221 std::string sectionName = pv::sectionTitles.count(sectionID)
222 ? pv::sectionTitles.at(sectionID)
223 : "Unknown Section";
Matt Spinleracb7c102020-01-10 13:49:22 -0600224
225 // Add a count if there are multiple of this type of section
226 auto count = pluralSections.find(section.header().id);
227 if (count != pluralSections.end())
228 {
229 sectionName += " " + std::to_string(count->second);
230 count->second++;
231 }
232
Aatir186ce8c2019-10-20 15:13:39 -0500233 if (section.valid())
234 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800235 auto json = (sectionID == "PS" || sectionID == "SS")
236 ? section.getJSON(registry)
237 : section.getJSON();
Matt Spinler4220a152020-03-26 10:18:09 -0500238
239 buf += "\"" + sectionName + "\": {\n";
240
Aatir Manzurad0e0472019-10-07 13:18:37 -0500241 if (json)
242 {
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800243 buf += *json + "\n},\n";
Aatir Manzurad0e0472019-10-07 13:18:37 -0500244 }
245 else
246 {
Matt Spinler4220a152020-03-26 10:18:09 -0500247 jsonInsert(buf, pv::sectionVer,
248 getNumberString("%d", section.header().version), 1);
249 jsonInsert(buf, pv::subSection,
250 getNumberString("%d", section.header().subType), 1);
251 jsonInsert(buf, pv::createdBy,
252 getNumberString("0x%X", section.header().componentID),
253 1);
254
Aatir Manzurad0e0472019-10-07 13:18:37 -0500255 std::vector<uint8_t> data;
256 Stream s{data};
257 section.flatten(s);
Matt Spinler4220a152020-03-26 10:18:09 -0500258 std::string dstr =
259 dumpHex(std::data(data) + SectionHeader::flattenedSize(),
260 data.size(), 2);
261
262 std::string jsonIndent(indentLevel, 0x20);
263 buf += jsonIndent + "\"Data\": [\n";
264 buf += dstr;
265 buf += jsonIndent + "]\n";
266 buf += "},\n";
Aatir Manzurad0e0472019-10-07 13:18:37 -0500267 }
Aatir186ce8c2019-10-20 15:13:39 -0500268 }
269 else
270 {
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800271 buf += "\n\"Invalid Section\": [\n \"invalid\"\n],\n";
Aatir186ce8c2019-10-20 15:13:39 -0500272 }
273}
274
Matt Spinleracb7c102020-01-10 13:49:22 -0600275std::map<uint16_t, size_t> PEL::getPluralSections() const
276{
277 std::map<uint16_t, size_t> sectionCounts;
278
279 for (const auto& section : optionalSections())
280 {
281 if (sectionCounts.find(section->header().id) == sectionCounts.end())
282 {
283 sectionCounts[section->header().id] = 1;
284 }
285 else
286 {
287 sectionCounts[section->header().id]++;
288 }
289 }
290
291 std::map<uint16_t, size_t> sections;
292 for (const auto& [id, count] : sectionCounts)
293 {
294 if (count > 1)
295 {
296 // Start with 0 here and printSectionInJSON()
297 // will increment it as it goes.
298 sections.emplace(id, 0);
299 }
300 }
301
302 return sections;
303}
304
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800305void PEL::toJSON(message::Registry& registry) const
Aatir186ce8c2019-10-20 15:13:39 -0500306{
Matt Spinleracb7c102020-01-10 13:49:22 -0600307 auto sections = getPluralSections();
308
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800309 std::string buf = "{\n";
310 printSectionInJSON(*(_ph.get()), buf, sections, registry);
311 printSectionInJSON(*(_uh.get()), buf, sections, registry);
Aatir186ce8c2019-10-20 15:13:39 -0500312 for (auto& section : this->optionalSections())
313 {
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800314 printSectionInJSON(*(section.get()), buf, sections, registry);
Aatir186ce8c2019-10-20 15:13:39 -0500315 }
316 buf += "}";
317 std::size_t found = buf.rfind(",");
318 if (found != std::string::npos)
319 buf.replace(found, 1, "");
320 std::cout << buf << std::endl;
321}
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800322
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600323namespace util
324{
325
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600326std::unique_ptr<UserData> makeJSONUserDataSection(const nlohmann::json& json)
327{
328 auto jsonString = json.dump();
329 std::vector<uint8_t> jsonData(jsonString.begin(), jsonString.end());
330
331 // Pad to a 4 byte boundary
332 while ((jsonData.size() % 4) != 0)
333 {
334 jsonData.push_back(0);
335 }
336
337 return std::make_unique<UserData>(
338 static_cast<uint16_t>(ComponentID::phosphorLogging),
339 static_cast<uint8_t>(UserDataFormat::json),
340 static_cast<uint8_t>(UserDataFormatVersion::json), jsonData);
341}
342
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600343std::unique_ptr<UserData> makeADUserDataSection(const AdditionalData& ad)
344{
345 assert(!ad.empty());
346 nlohmann::json json;
347
348 // Remove the 'ESEL' entry, as it contains a full PEL in the value.
349 if (ad.getValue("ESEL"))
350 {
351 auto newAD = ad;
352 newAD.remove("ESEL");
353 json = newAD.toJSON();
354 }
355 else
356 {
357 json = ad.toJSON();
358 }
359
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600360 return makeJSONUserDataSection(json);
361}
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600362
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600363void addProcessNameToJSON(nlohmann::json& json,
364 const std::optional<std::string>& pid,
365 const DataInterfaceBase& dataIface)
366{
Matt Spinler677381b2020-01-23 10:04:29 -0600367 std::string name{unknownValue};
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600368
369 try
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600370 {
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600371 if (pid)
372 {
373 auto n = dataIface.getProcessName(*pid);
374 if (n)
375 {
376 name = *n;
377 }
378 }
379 }
380 catch (std::exception& e)
381 {
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600382 }
383
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600384 json["Process Name"] = std::move(name);
385}
386
Matt Spinler677381b2020-01-23 10:04:29 -0600387void addBMCFWVersionIDToJSON(nlohmann::json& json,
388 const DataInterfaceBase& dataIface)
389{
390 auto id = dataIface.getBMCFWVersionID();
391 if (id.empty())
392 {
393 id = unknownValue;
394 }
395
396 json["BMC Version ID"] = std::move(id);
397}
398
Matt Spinler4aa23a12020-02-03 15:05:09 -0600399std::string lastSegment(char separator, std::string data)
400{
401 auto pos = data.find_last_of(separator);
402 if (pos != std::string::npos)
403 {
404 data = data.substr(pos + 1);
405 }
406
407 return data;
408}
409
410void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface)
411{
412 json["BMCState"] = lastSegment('.', dataIface.getBMCState());
413 json["ChassisState"] = lastSegment('.', dataIface.getChassisState());
414 json["HostState"] = lastSegment('.', dataIface.getHostState());
415}
416
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600417std::unique_ptr<UserData>
418 makeSysInfoUserDataSection(const AdditionalData& ad,
419 const DataInterfaceBase& dataIface)
420{
421 nlohmann::json json;
422
423 addProcessNameToJSON(json, ad.getValue("_PID"), dataIface);
Matt Spinler677381b2020-01-23 10:04:29 -0600424 addBMCFWVersionIDToJSON(json, dataIface);
Matt Spinler4aa23a12020-02-03 15:05:09 -0600425 addStatesToJSON(json, dataIface);
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600426
427 return makeJSONUserDataSection(json);
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600428}
429
430} // namespace util
431
Matt Spinlercb6b0592019-07-16 15:58:51 -0500432} // namespace pels
433} // namespace openpower