blob: 689640e3711a1ced55a2b30cefcad994abb577da [file] [log] [blame]
Alexander Hansen40fb5492025-10-28 17:56:12 +01001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright 2019 IBM Corporation
3
Jayanth Othayothda9b5832021-11-05 04:19:43 -05004#include "config.h"
5
Matt Spinlercb6b0592019-07-16 15:58:51 -05006#include "pel.hpp"
7
8#include "bcd_time.hpp"
Matt Spinler386a61e2020-08-13 15:51:12 -05009#include "extended_user_data.hpp"
Matt Spinlerc63e2e82019-12-02 15:50:12 -060010#include "extended_user_header.hpp"
Matt Spinleraa659472019-10-23 09:26:48 -050011#include "failing_mtms.hpp"
Andrew Geisslerf8e750d2022-01-14 14:56:13 -060012#include "fru_identity.hpp"
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +080013#include "json_utils.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050014#include "log_id.hpp"
Matt Spinlerf1e85e22019-11-01 11:31:31 -050015#include "pel_rules.hpp"
Aatir186ce8c2019-10-20 15:13:39 -050016#include "pel_values.hpp"
Matt Spinler131870c2019-09-25 13:29:04 -050017#include "section_factory.hpp"
Matt Spinlerbd716f02019-10-15 10:54:11 -050018#include "src.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050019#include "stream.hpp"
Matt Spinlerafa857c2019-10-24 13:03:46 -050020#include "user_data_formats.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050021
Jayanth Othayoth92b20662021-11-05 00:09:15 -050022#ifdef PEL_ENABLE_PHAL
Jayanth Othayothda9b5832021-11-05 04:19:43 -050023#include "phal_service_actions.hpp"
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050024#include "sbe_ffdc_handler.hpp"
Deepa Karthikeyanff35be32024-10-15 09:10:49 -050025
26#include <libguard/guard_interface.hpp>
27#include <libguard/include/guard_record.hpp>
28
29namespace libguard = openpower::guard;
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050030#endif
31
Matt Spinler5b289b22020-03-26 14:27:19 -050032#include <sys/stat.h>
33#include <unistd.h>
34
Matt Spinlerfd2da662023-07-07 16:25:14 -050035#include <phosphor-logging/lg2.hpp>
Matt Spinler07eefc52019-09-26 11:18:26 -050036
Jayanth Othayoth1aa90d42023-09-13 04:25:45 -050037#include <format>
Patrick Williams2544b412022-10-04 08:41:06 -050038#include <iostream>
Arya K Padmand8ae6182024-07-19 06:25:10 -050039#include <ranges>
Patrick Williams2544b412022-10-04 08:41:06 -050040
Matt Spinlercb6b0592019-07-16 15:58:51 -050041namespace openpower
42{
43namespace pels
44{
Aatir186ce8c2019-10-20 15:13:39 -050045namespace pv = openpower::pels::pel_values;
Matt Spinlerb8323632019-09-20 15:11:04 -050046
Matt Spinler677381b2020-01-23 10:04:29 -060047constexpr auto unknownValue = "Unknown";
Arya K Padmand8ae6182024-07-19 06:25:10 -050048constexpr auto AdDIMMInfoFetchError = "DIMMs Info Fetch Error";
Matt Spinler677381b2020-01-23 10:04:29 -060049
Matt Spinler4bfc9082020-03-24 15:05:54 -050050PEL::PEL(const message::Entry& regEntry, uint32_t obmcLogID, uint64_t timestamp,
Matt Spinlerbd716f02019-10-15 10:54:11 -050051 phosphor::logging::Entry::Level severity,
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050052 const AdditionalData& additionalData, const PelFFDC& ffdcFilesIn,
Matt Spinler9d921092022-12-15 11:54:49 -060053 const DataInterfaceBase& dataIface, const JournalBase& journal)
Matt Spinlerb8323632019-09-20 15:11:04 -050054{
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050055 // No changes in input, for non SBE error related requests
56 PelFFDC ffdcFiles = ffdcFilesIn;
57
Jayanth Othayoth92b20662021-11-05 00:09:15 -050058#ifdef PEL_ENABLE_PHAL
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050059 // Add sbe ffdc processed data into ffdcfiles.
60 namespace sbe = openpower::pels::sbe;
Patrick Williams075c7922024-08-16 15:19:49 -040061 auto processReq =
62 std::any_of(ffdcFiles.begin(), ffdcFiles.end(), [](const auto& file) {
63 return file.format == UserDataFormat::custom &&
64 file.subType == sbe::sbeFFDCSubType;
65 });
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050066 // sbeFFDC can't be destroyed until the end of the PEL constructor
67 // because it needs to keep around the FFDC Files to be used below.
68 std::unique_ptr<sbe::SbeFFDC> sbeFFDCPtr;
69 if (processReq)
70 {
Patrick Williams075c7922024-08-16 15:19:49 -040071 sbeFFDCPtr =
72 std::make_unique<sbe::SbeFFDC>(additionalData, ffdcFilesIn);
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050073 const auto& sbeFFDCFiles = sbeFFDCPtr->getSbeFFDC();
74 ffdcFiles.insert(ffdcFiles.end(), sbeFFDCFiles.begin(),
75 sbeFFDCFiles.end());
Jayanth Othayoth742b00b2022-06-30 05:16:57 -050076
77 // update pel priority for spare clock failures
78 if (auto customSeverity = sbeFFDCPtr->getSeverity())
79 {
80 severity = customSeverity.value();
81 }
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050082 }
83#endif
84
Arya K Padmand8ae6182024-07-19 06:25:10 -050085 DebugData debugData;
Matt Spinler5a90a952020-08-27 09:39:03 -050086 nlohmann::json callouts;
Matt Spinler85f61a62020-06-03 16:28:55 -050087
Matt Spinler4bfc9082020-03-24 15:05:54 -050088 _ph = std::make_unique<PrivateHeader>(regEntry.componentID, obmcLogID,
Matt Spinlerb8323632019-09-20 15:11:04 -050089 timestamp);
Vijay Lobo6b3f3452021-04-15 23:04:42 -050090 _uh = std::make_unique<UserHeader>(regEntry, severity, additionalData,
91 dataIface);
Matt Spinlerb8323632019-09-20 15:11:04 -050092
Matt Spinler5a90a952020-08-27 09:39:03 -050093 // Extract any callouts embedded in an FFDC file.
94 if (!ffdcFiles.empty())
95 {
96 try
97 {
98 callouts = getCalloutJSON(ffdcFiles);
99 }
100 catch (const std::exception& e)
101 {
102 debugData.emplace("FFDC file JSON callouts error",
103 std::vector<std::string>{e.what()});
104 }
105 }
106
Patrick Williams075c7922024-08-16 15:19:49 -0400107 auto src =
108 std::make_unique<SRC>(regEntry, additionalData, callouts, dataIface);
Matt Spinler5a90a952020-08-27 09:39:03 -0500109
Arya K Padmand8ae6182024-07-19 06:25:10 -0500110 nlohmann::json adSysInfoData(nlohmann::json::value_t::object);
111 addAdDetailsForDIMMsCallout(src, dataIface, adSysInfoData, debugData);
112
Matt Spinler85f61a62020-06-03 16:28:55 -0500113 if (!src->getDebugData().empty())
114 {
115 // Something didn't go as planned
116 debugData.emplace("SRC", src->getDebugData());
117 }
Matt Spinlerc63e2e82019-12-02 15:50:12 -0600118
Matt Spinler4bfc9082020-03-24 15:05:54 -0500119 auto euh = std::make_unique<ExtendedUserHeader>(dataIface, regEntry, *src);
Matt Spinlerc63e2e82019-12-02 15:50:12 -0600120
Matt Spinlerbd716f02019-10-15 10:54:11 -0500121 _optionalSections.push_back(std::move(src));
Matt Spinlerc63e2e82019-12-02 15:50:12 -0600122 _optionalSections.push_back(std::move(euh));
Matt Spinlerb8323632019-09-20 15:11:04 -0500123
Matt Spinleraa659472019-10-23 09:26:48 -0500124 auto mtms = std::make_unique<FailingMTMS>(dataIface);
125 _optionalSections.push_back(std::move(mtms));
Matt Spinlerbd716f02019-10-15 10:54:11 -0500126
Arya K Padmand8ae6182024-07-19 06:25:10 -0500127 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface, true,
128 adSysInfoData);
Matt Spinler85f61a62020-06-03 16:28:55 -0500129 addUserDataSection(std::move(ud));
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600130
Sumit Kumar3e274432021-09-14 06:37:56 -0500131 // Check for pel severity of type - 0x51 = critical error, system
132 // termination and update terminate bit in SRC for pels
133 updateTerminateBitInSRCSection();
134
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500135 // Create a UserData section from AdditionalData.
Matt Spinlerafa857c2019-10-24 13:03:46 -0500136 if (!additionalData.empty())
137 {
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600138 ud = util::makeADUserDataSection(additionalData);
Matt Spinler85f61a62020-06-03 16:28:55 -0500139 addUserDataSection(std::move(ud));
Matt Spinlerafa857c2019-10-24 13:03:46 -0500140 }
141
Matt Spinler56ad2a02020-03-26 14:00:52 -0500142 // Add any FFDC files into UserData sections
143 for (const auto& file : ffdcFiles)
144 {
145 ud = util::makeFFDCuserDataSection(regEntry.componentID, file);
146 if (!ud)
147 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500148 // Add this error into the debug data UserData section
149 std::ostringstream msg;
150 msg << "Could not make PEL FFDC UserData section from file"
151 << std::hex << regEntry.componentID << " " << file.subType
152 << " " << file.version;
153 if (debugData.count("FFDC File"))
154 {
155 debugData.at("FFDC File").push_back(msg.str());
156 }
157 else
158 {
159 debugData.emplace("FFDC File",
160 std::vector<std::string>{msg.str()});
161 }
162
Matt Spinler56ad2a02020-03-26 14:00:52 -0500163 continue;
164 }
165
Matt Spinler85f61a62020-06-03 16:28:55 -0500166 addUserDataSection(std::move(ud));
167 }
Matt Spinler56ad2a02020-03-26 14:00:52 -0500168
Jayanth Othayothda9b5832021-11-05 04:19:43 -0500169#ifdef PEL_ENABLE_PHAL
170 auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID);
Deepa Karthikeyanff35be32024-10-15 09:10:49 -0500171 openpower::pels::phal::createServiceActions(callouts, dataIface, plid());
Jayanth Othayothda9b5832021-11-05 04:19:43 -0500172#endif
173
Matt Spinler85f61a62020-06-03 16:28:55 -0500174 // Store in the PEL any important debug data created while
175 // building the PEL sections.
176 if (!debugData.empty())
177 {
178 nlohmann::json data;
179 data["PEL Internal Debug Data"] = debugData;
180 ud = util::makeJSONUserDataSection(data);
181
182 addUserDataSection(std::move(ud));
183
184 // Also put in the journal for debug
Matt Spinler45796e82022-07-01 11:25:27 -0500185 for (const auto& [name, msgs] : debugData)
Matt Spinler85f61a62020-06-03 16:28:55 -0500186 {
Matt Spinler45796e82022-07-01 11:25:27 -0500187 for (const auto& message : msgs)
Matt Spinler85f61a62020-06-03 16:28:55 -0500188 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500189 lg2::info("{NAME}: {MSG}", "NAME", name, "MSG", message);
Matt Spinler56ad2a02020-03-26 14:00:52 -0500190 }
191 }
Matt Spinler56ad2a02020-03-26 14:00:52 -0500192 }
193
Matt Spinler9d921092022-12-15 11:54:49 -0600194 addJournalSections(regEntry, journal);
195
Matt Spinler97d19b42019-10-29 11:34:03 -0500196 _ph->setSectionCount(2 + _optionalSections.size());
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500197
198 checkRulesAndFix();
Matt Spinlerb8323632019-09-20 15:11:04 -0500199}
Matt Spinlercb6b0592019-07-16 15:58:51 -0500200
Patrick Williams2544b412022-10-04 08:41:06 -0500201PEL::PEL(std::vector<uint8_t>& data) : PEL(data, 0) {}
Matt Spinlercb6b0592019-07-16 15:58:51 -0500202
Matt Spinler07eefc52019-09-26 11:18:26 -0500203PEL::PEL(std::vector<uint8_t>& data, uint32_t obmcLogID)
Matt Spinlercb6b0592019-07-16 15:58:51 -0500204{
Matt Spinler07eefc52019-09-26 11:18:26 -0500205 populateFromRawData(data, obmcLogID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500206}
207
Matt Spinler07eefc52019-09-26 11:18:26 -0500208void PEL::populateFromRawData(std::vector<uint8_t>& data, uint32_t obmcLogID)
Matt Spinlercb6b0592019-07-16 15:58:51 -0500209{
Matt Spinler07eefc52019-09-26 11:18:26 -0500210 Stream pelData{data};
Matt Spinlercb6b0592019-07-16 15:58:51 -0500211 _ph = std::make_unique<PrivateHeader>(pelData);
212 if (obmcLogID != 0)
213 {
Matt Spinler97d19b42019-10-29 11:34:03 -0500214 _ph->setOBMCLogID(obmcLogID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500215 }
216
217 _uh = std::make_unique<UserHeader>(pelData);
Matt Spinler131870c2019-09-25 13:29:04 -0500218
219 // Use the section factory to create the rest of the objects
220 for (size_t i = 2; i < _ph->sectionCount(); i++)
221 {
222 auto section = section_factory::create(pelData);
223 _optionalSections.push_back(std::move(section));
224 }
Matt Spinlercb6b0592019-07-16 15:58:51 -0500225}
226
227bool PEL::valid() const
228{
229 bool valid = _ph->valid();
230
231 if (valid)
232 {
233 valid = _uh->valid();
234 }
235
Matt Spinler131870c2019-09-25 13:29:04 -0500236 if (valid)
237 {
238 if (!std::all_of(_optionalSections.begin(), _optionalSections.end(),
239 [](const auto& section) { return section->valid(); }))
240 {
241 valid = false;
242 }
243 }
244
Matt Spinlercb6b0592019-07-16 15:58:51 -0500245 return valid;
246}
247
248void PEL::setCommitTime()
249{
250 auto now = std::chrono::system_clock::now();
Matt Spinler97d19b42019-10-29 11:34:03 -0500251 _ph->setCommitTimestamp(getBCDTime(now));
Matt Spinlercb6b0592019-07-16 15:58:51 -0500252}
253
254void PEL::assignID()
255{
Matt Spinler97d19b42019-10-29 11:34:03 -0500256 _ph->setID(generatePELID());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500257}
258
Matt Spinler06885452019-11-06 10:35:42 -0600259void PEL::flatten(std::vector<uint8_t>& pelBuffer) const
Matt Spinlercb6b0592019-07-16 15:58:51 -0500260{
261 Stream pelData{pelBuffer};
Matt Spinlerb8323632019-09-20 15:11:04 -0500262
Matt Spinler07eefc52019-09-26 11:18:26 -0500263 if (!valid())
Matt Spinlercb6b0592019-07-16 15:58:51 -0500264 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500265 lg2::warning("Unflattening an invalid PEL");
Matt Spinlercb6b0592019-07-16 15:58:51 -0500266 }
267
Matt Spinler07eefc52019-09-26 11:18:26 -0500268 _ph->flatten(pelData);
Matt Spinlerb8323632019-09-20 15:11:04 -0500269 _uh->flatten(pelData);
Matt Spinler07eefc52019-09-26 11:18:26 -0500270
271 for (auto& section : _optionalSections)
272 {
273 section->flatten(pelData);
274 }
Matt Spinlercb6b0592019-07-16 15:58:51 -0500275}
276
Matt Spinler06885452019-11-06 10:35:42 -0600277std::vector<uint8_t> PEL::data() const
Matt Spinlercb6b0592019-07-16 15:58:51 -0500278{
Matt Spinler07eefc52019-09-26 11:18:26 -0500279 std::vector<uint8_t> pelData;
280 flatten(pelData);
281 return pelData;
Matt Spinlercb6b0592019-07-16 15:58:51 -0500282}
283
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600284size_t PEL::size() const
285{
286 size_t size = 0;
287
288 if (_ph)
289 {
290 size += _ph->header().size;
291 }
292
293 if (_uh)
294 {
295 size += _uh->header().size;
296 }
297
298 for (const auto& section : _optionalSections)
299 {
300 size += section->header().size;
301 }
302
303 return size;
304}
305
Matt Spinlerbd716f02019-10-15 10:54:11 -0500306std::optional<SRC*> PEL::primarySRC() const
307{
Patrick Williams075c7922024-08-16 15:19:49 -0400308 auto src = std::find_if(
309 _optionalSections.begin(), _optionalSections.end(), [](auto& section) {
310 return section->header().id ==
311 static_cast<uint16_t>(SectionID::primarySRC);
312 });
Matt Spinlerbd716f02019-10-15 10:54:11 -0500313 if (src != _optionalSections.end())
314 {
315 return static_cast<SRC*>(src->get());
316 }
317
318 return std::nullopt;
319}
320
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500321void PEL::checkRulesAndFix()
322{
Matt Spinler1f93c592020-09-10 10:43:08 -0500323 // Only fix if the action flags are at their default value which
324 // means they weren't specified in the registry. Otherwise
325 // assume the user knows what they are doing.
326 if (_uh->actionFlags() == actionFlagsDefault)
327 {
Patrick Williams075c7922024-08-16 15:19:49 -0400328 auto [actionFlags, eventType] =
329 pel_rules::check(0, _uh->eventType(), _uh->severity());
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500330
Matt Spinler1f93c592020-09-10 10:43:08 -0500331 _uh->setActionFlags(actionFlags);
332 _uh->setEventType(eventType);
333 }
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500334}
335
Patrick Williams075c7922024-08-16 15:19:49 -0400336void PEL::printSectionInJSON(
337 const Section& section, std::string& buf,
338 std::map<uint16_t, size_t>& pluralSections, message::Registry& registry,
339 const std::vector<std::string>& plugins, uint8_t creatorID) const
Aatir186ce8c2019-10-20 15:13:39 -0500340{
341 char tmpB[5];
Aatir Manzurad0e0472019-10-07 13:18:37 -0500342 uint8_t id[] = {static_cast<uint8_t>(section.header().id >> 8),
343 static_cast<uint8_t>(section.header().id)};
344 sprintf(tmpB, "%c%c", id[0], id[1]);
345 std::string sectionID(tmpB);
346 std::string sectionName = pv::sectionTitles.count(sectionID)
347 ? pv::sectionTitles.at(sectionID)
348 : "Unknown Section";
Matt Spinleracb7c102020-01-10 13:49:22 -0600349
350 // Add a count if there are multiple of this type of section
351 auto count = pluralSections.find(section.header().id);
352 if (count != pluralSections.end())
353 {
354 sectionName += " " + std::to_string(count->second);
355 count->second++;
356 }
357
Aatir186ce8c2019-10-20 15:13:39 -0500358 if (section.valid())
359 {
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800360 std::optional<std::string> json;
361 if (sectionID == "PS" || sectionID == "SS")
362 {
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800363 json = section.getJSON(registry, plugins, creatorID);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800364 }
Matt Spinler386a61e2020-08-13 15:51:12 -0500365 else if ((sectionID == "UD") || (sectionID == "ED"))
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800366 {
Harisuddin Mohamed Isa3fdcd4e2020-08-26 11:56:42 +0800367 json = section.getJSON(creatorID, plugins);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800368 }
369 else
370 {
Matt Spinlerb832aa52023-03-21 15:32:34 -0500371 json = section.getJSON(creatorID);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800372 }
Matt Spinler4220a152020-03-26 10:18:09 -0500373
374 buf += "\"" + sectionName + "\": {\n";
375
Aatir Manzurad0e0472019-10-07 13:18:37 -0500376 if (json)
377 {
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800378 buf += *json + "\n},\n";
Aatir Manzurad0e0472019-10-07 13:18:37 -0500379 }
380 else
381 {
Matt Spinler4220a152020-03-26 10:18:09 -0500382 jsonInsert(buf, pv::sectionVer,
383 getNumberString("%d", section.header().version), 1);
384 jsonInsert(buf, pv::subSection,
385 getNumberString("%d", section.header().subType), 1);
386 jsonInsert(buf, pv::createdBy,
387 getNumberString("0x%X", section.header().componentID),
388 1);
389
Aatir Manzurad0e0472019-10-07 13:18:37 -0500390 std::vector<uint8_t> data;
391 Stream s{data};
392 section.flatten(s);
Matt Spinler4220a152020-03-26 10:18:09 -0500393 std::string dstr =
394 dumpHex(std::data(data) + SectionHeader::flattenedSize(),
Arya K Padman8c8aaa02024-04-28 23:23:45 -0500395 data.size() - SectionHeader::flattenedSize(), 2)
396 .get();
Matt Spinler4220a152020-03-26 10:18:09 -0500397 std::string jsonIndent(indentLevel, 0x20);
398 buf += jsonIndent + "\"Data\": [\n";
399 buf += dstr;
400 buf += jsonIndent + "]\n";
401 buf += "},\n";
Aatir Manzurad0e0472019-10-07 13:18:37 -0500402 }
Aatir186ce8c2019-10-20 15:13:39 -0500403 }
404 else
405 {
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800406 buf += "\n\"Invalid Section\": [\n \"invalid\"\n],\n";
Aatir186ce8c2019-10-20 15:13:39 -0500407 }
408}
409
Matt Spinleracb7c102020-01-10 13:49:22 -0600410std::map<uint16_t, size_t> PEL::getPluralSections() const
411{
412 std::map<uint16_t, size_t> sectionCounts;
413
414 for (const auto& section : optionalSections())
415 {
416 if (sectionCounts.find(section->header().id) == sectionCounts.end())
417 {
418 sectionCounts[section->header().id] = 1;
419 }
420 else
421 {
422 sectionCounts[section->header().id]++;
423 }
424 }
425
426 std::map<uint16_t, size_t> sections;
427 for (const auto& [id, count] : sectionCounts)
428 {
429 if (count > 1)
430 {
431 // Start with 0 here and printSectionInJSON()
432 // will increment it as it goes.
433 sections.emplace(id, 0);
434 }
435 }
436
437 return sections;
438}
439
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800440void PEL::toJSON(message::Registry& registry,
441 const std::vector<std::string>& plugins) const
Aatir186ce8c2019-10-20 15:13:39 -0500442{
Matt Spinleracb7c102020-01-10 13:49:22 -0600443 auto sections = getPluralSections();
444
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800445 std::string buf = "{\n";
Matt Spinlerb832aa52023-03-21 15:32:34 -0500446 printSectionInJSON(*(_ph.get()), buf, sections, registry, plugins,
447 _ph->creatorID());
448 printSectionInJSON(*(_uh.get()), buf, sections, registry, plugins,
449 _ph->creatorID());
Aatir186ce8c2019-10-20 15:13:39 -0500450 for (auto& section : this->optionalSections())
451 {
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800452 printSectionInJSON(*(section.get()), buf, sections, registry, plugins,
453 _ph->creatorID());
Aatir186ce8c2019-10-20 15:13:39 -0500454 }
455 buf += "}";
456 std::size_t found = buf.rfind(",");
457 if (found != std::string::npos)
458 buf.replace(found, 1, "");
459 std::cout << buf << std::endl;
460}
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800461
Matt Spinler85f61a62020-06-03 16:28:55 -0500462bool PEL::addUserDataSection(std::unique_ptr<UserData> userData)
463{
464 if (size() + userData->header().size > _maxPELSize)
465 {
466 if (userData->shrink(_maxPELSize - size()))
467 {
468 _optionalSections.push_back(std::move(userData));
469 }
470 else
471 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500472 lg2::warning("Could not shrink UserData section. Dropping. "
473 "Section size = {SSIZE}, Component ID = {COMP_ID}, "
474 "Subtype = {SUBTYPE}, Version = {VERSION}",
475 "SSIZE", userData->header().size, "COMP_ID",
476 userData->header().componentID, "SUBTYPE",
477 userData->header().subType, "VERSION",
478 userData->header().version);
Matt Spinler85f61a62020-06-03 16:28:55 -0500479 return false;
480 }
481 }
482 else
483 {
484 _optionalSections.push_back(std::move(userData));
485 }
486 return true;
487}
488
Matt Spinler5a90a952020-08-27 09:39:03 -0500489nlohmann::json PEL::getCalloutJSON(const PelFFDC& ffdcFiles)
490{
491 nlohmann::json callouts;
492
493 for (const auto& file : ffdcFiles)
494 {
495 if ((file.format == UserDataFormat::json) &&
496 (file.subType == jsonCalloutSubtype))
497 {
498 auto data = util::readFD(file.fd);
499 if (data.empty())
500 {
501 throw std::runtime_error{
502 "Could not get data from JSON callout file descriptor"};
503 }
504
505 std::string jsonString{data.begin(), data.begin() + data.size()};
506
507 callouts = nlohmann::json::parse(jsonString);
508 break;
509 }
510 }
511
512 return callouts;
513}
514
Andrew Geisslerf8e750d2022-01-14 14:56:13 -0600515bool PEL::isHwCalloutPresent() const
Andrew Geissler44fc3162020-07-09 09:21:31 -0500516{
517 auto pSRC = primarySRC();
518 if (!pSRC)
519 {
520 return false;
521 }
522
523 bool calloutPresent = false;
524 if ((*pSRC)->callouts())
525 {
526 for (auto& i : (*pSRC)->callouts()->callouts())
527 {
528 if (((*i).fruIdentity()))
529 {
530 auto& fruId = (*i).fruIdentity();
Andrew Geisslerf8e750d2022-01-14 14:56:13 -0600531 if ((*fruId).failingComponentType() ==
532 src::FRUIdentity::hardwareFRU)
Andrew Geissler44fc3162020-07-09 09:21:31 -0500533 {
534 calloutPresent = true;
535 break;
536 }
537 }
538 }
539 }
540
541 return calloutPresent;
542}
543
Sumit Kumar3160a542021-04-26 08:07:04 -0500544void PEL::updateSysInfoInExtendedUserDataSection(
545 const DataInterfaceBase& dataIface)
546{
547 const AdditionalData additionalData;
548
549 // Check for PEL from Hostboot
550 if (_ph->creatorID() == static_cast<uint8_t>(CreatorID::hostboot))
551 {
552 // Get the ED section from PEL
Patrick Williams075c7922024-08-16 15:19:49 -0400553 auto op = std::find_if(
554 _optionalSections.begin(), _optionalSections.end(),
555 [](auto& section) {
556 return section->header().id ==
557 static_cast<uint16_t>(SectionID::extUserData);
558 });
Sumit Kumar3160a542021-04-26 08:07:04 -0500559
560 // Check for ED section found and its not the last section of PEL
561 if (op != _optionalSections.end())
562 {
563 // Get the extended user data class mapped to found section
564 auto extUserData = static_cast<ExtendedUserData*>(op->get());
565
566 // Check for the creator ID is for OpenBMC
567 if (extUserData->creatorID() ==
568 static_cast<uint8_t>(CreatorID::openBMC))
569 {
570 // Update subtype and component id
571 auto subType = static_cast<uint8_t>(UserDataFormat::json);
572 auto componentId =
573 static_cast<uint16_t>(ComponentID::phosphorLogging);
574
575 // Update system data to ED section
George Liu9ac0d9b2022-07-15 10:57:38 +0800576 auto ud = util::makeSysInfoUserDataSection(additionalData,
577 dataIface, false);
Sumit Kumar3160a542021-04-26 08:07:04 -0500578 extUserData->updateDataSection(subType, componentId,
579 ud->data());
580 }
581 }
582 }
583}
584
Matt Spinler8e65f4e2023-05-02 13:40:08 -0500585bool PEL::getDeconfigFlag() const
586{
587 auto creator = static_cast<CreatorID>(_ph->creatorID());
588
589 if ((creator == CreatorID::openBMC) || (creator == CreatorID::hostboot))
590 {
591 auto src = primarySRC();
592 return (*src)->getErrorStatusFlag(SRC::ErrorStatusFlags::deconfigured);
593 }
594 return false;
595}
596
597bool PEL::getGuardFlag() const
598{
599 auto creator = static_cast<CreatorID>(_ph->creatorID());
600
601 if ((creator == CreatorID::openBMC) || (creator == CreatorID::hostboot))
602 {
603 auto src = primarySRC();
604 return (*src)->getErrorStatusFlag(SRC::ErrorStatusFlags::guarded);
605 }
606 return false;
607}
608
Sumit Kumar3e274432021-09-14 06:37:56 -0500609void PEL::updateTerminateBitInSRCSection()
610{
611 // Check for pel severity of type - 0x51 = critical error, system
612 // termination
613 if (_uh->severity() == 0x51)
614 {
615 // Get the primary SRC section
616 auto pSRC = primarySRC();
617 if (pSRC)
618 {
619 (*pSRC)->setTerminateBit();
620 }
621 }
622}
623
Matt Spinler9d921092022-12-15 11:54:49 -0600624void PEL::addJournalSections(const message::Entry& regEntry,
625 const JournalBase& journal)
626{
627 if (!regEntry.journalCapture)
628 {
629 return;
630 }
631
632 // Write all unwritten journal data to disk.
633 journal.sync();
634
635 const auto& jc = regEntry.journalCapture.value();
636 std::vector<std::vector<std::string>> allMessages;
637
638 if (std::holds_alternative<size_t>(jc))
639 {
640 // Get the previous numLines journal entries
641 const auto& numLines = std::get<size_t>(jc);
642 try
643 {
644 auto messages = journal.getMessages("", numLines);
645 if (!messages.empty())
646 {
647 allMessages.push_back(std::move(messages));
648 }
649 }
650 catch (const std::exception& e)
651 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500652 lg2::error("Failed during journal collection: {ERROR}", "ERROR", e);
Matt Spinler9d921092022-12-15 11:54:49 -0600653 }
654 }
655 else if (std::holds_alternative<message::AppCaptureList>(jc))
656 {
657 // Get journal entries based on the syslog id field.
658 const auto& sections = std::get<message::AppCaptureList>(jc);
659 for (const auto& [syslogID, numLines] : sections)
660 {
661 try
662 {
663 auto messages = journal.getMessages(syslogID, numLines);
664 if (!messages.empty())
665 {
666 allMessages.push_back(std::move(messages));
667 }
668 }
669 catch (const std::exception& e)
670 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500671 lg2::error("Failed during journal collection: {ERROR}", "ERROR",
672 e);
Matt Spinler9d921092022-12-15 11:54:49 -0600673 }
674 }
675 }
676
677 // Create the UserData sections
678 for (const auto& messages : allMessages)
679 {
680 auto buffer = util::flattenLines(messages);
681
682 // If the buffer is way too big, it can overflow the uint16_t
683 // PEL section size field that is checked below so do a cursory
684 // check here.
685 if (buffer.size() > _maxPELSize)
686 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500687 lg2::warning(
688 "Journal UserData section does not fit in PEL, dropping. "
689 "PEL size = {PEL_SIZE}, data size = {DATA_SIZE}",
690 "PEL_SIZE", size(), "DATA_SIZE", buffer.size());
Matt Spinler9d921092022-12-15 11:54:49 -0600691 continue;
692 }
693
694 // Sections must be 4 byte aligned.
695 while (buffer.size() % 4 != 0)
696 {
697 buffer.push_back(0);
698 }
699
700 auto ud = std::make_unique<UserData>(
701 static_cast<uint16_t>(ComponentID::phosphorLogging),
702 static_cast<uint8_t>(UserDataFormat::text),
703 static_cast<uint8_t>(UserDataFormatVersion::text), buffer);
704
705 if (size() + ud->header().size <= _maxPELSize)
706 {
707 _optionalSections.push_back(std::move(ud));
708 }
709 else
710 {
711 // Don't attempt to shrink here since we'd be dropping the
712 // most recent journal entries which would be confusing.
Matt Spinlerfd2da662023-07-07 16:25:14 -0500713 lg2::warning(
714 "Journal UserData section does not fit in PEL, dropping. "
715 "PEL size = {PEL_SIZE}, data size = {DATA_SIZE}",
716 "PEL_SIZE", size(), "DATA_SIZE", buffer.size());
Matt Spinler9d921092022-12-15 11:54:49 -0600717 ud.reset();
718 continue;
719 }
720 }
721}
722
Arya K Padmand8ae6182024-07-19 06:25:10 -0500723void PEL::addAdDetailsForDIMMsCallout(
724 const std::unique_ptr<SRC>& src, const DataInterfaceBase& dataIface,
725 nlohmann::json& adSysInfoData, DebugData& debugData)
726{
727 if (!src->callouts())
728 {
729 // No callouts
730 return;
731 }
732
Matt Spinlercc06b7f2025-05-09 11:47:25 -0500733 auto isDIMMCallout = [&dataIface](const auto& callout) {
Arya K Padmand8ae6182024-07-19 06:25:10 -0500734 auto locCode{callout->locationCode()};
735 if (locCode.empty())
736 {
737 // Not a hardware callout. No action required
738 return false;
739 }
740 else
741 {
Arya K Padmanced8ed72024-09-02 05:18:07 -0500742 return const_cast<DataInterfaceBase&>(dataIface).isDIMM(locCode);
Arya K Padmand8ae6182024-07-19 06:25:10 -0500743 }
744 };
745 auto addAdDIMMDetails = [&dataIface, &adSysInfoData,
746 &debugData](const auto& callout) {
747 auto dimmLocCode{callout->locationCode()};
748
749 auto diPropVal = dataIface.getDIProperty(dimmLocCode);
750 if (!diPropVal.has_value())
751 {
752 std::string errMsg{
753 std::format("Failed reading DI property from "
754 "VINI Interface for the LocationCode:[{}]",
755 dimmLocCode)};
756 debugData[AdDIMMInfoFetchError].emplace_back(errMsg);
757 }
758 else
759 {
760 util::addDIMMInfo(dimmLocCode, diPropVal.value(), adSysInfoData);
761 }
762 };
763
764 auto DIMMsCallouts = src->callouts()->callouts() |
765 std::views::filter(isDIMMCallout);
766
767 std::ranges::for_each(DIMMsCallouts, addAdDIMMDetails);
768}
769
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600770namespace util
771{
772
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600773std::unique_ptr<UserData> makeJSONUserDataSection(const nlohmann::json& json)
774{
Matt Spinler13db1d32024-10-08 11:22:54 -0500775 std::string jsonString;
776 try
777 {
778 jsonString = json.dump();
779 }
780 catch (const std::exception& e)
781 {
782 lg2::error("json.dump() failed with: {ERROR}", "ERROR", e);
783 jsonString = "Invalid JSON passed to makeJSONUserDataSection!";
784 }
785
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600786 std::vector<uint8_t> jsonData(jsonString.begin(), jsonString.end());
787
788 // Pad to a 4 byte boundary
789 while ((jsonData.size() % 4) != 0)
790 {
791 jsonData.push_back(0);
792 }
793
794 return std::make_unique<UserData>(
795 static_cast<uint16_t>(ComponentID::phosphorLogging),
796 static_cast<uint8_t>(UserDataFormat::json),
797 static_cast<uint8_t>(UserDataFormatVersion::json), jsonData);
798}
799
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600800std::unique_ptr<UserData> makeADUserDataSection(const AdditionalData& ad)
801{
802 assert(!ad.empty());
803 nlohmann::json json;
804
805 // Remove the 'ESEL' entry, as it contains a full PEL in the value.
806 if (ad.getValue("ESEL"))
807 {
808 auto newAD = ad;
809 newAD.remove("ESEL");
810 json = newAD.toJSON();
811 }
812 else
813 {
814 json = ad.toJSON();
815 }
816
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600817 return makeJSONUserDataSection(json);
818}
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600819
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600820void addProcessNameToJSON(nlohmann::json& json,
821 const std::optional<std::string>& pid,
822 const DataInterfaceBase& dataIface)
823{
Matt Spinler677381b2020-01-23 10:04:29 -0600824 std::string name{unknownValue};
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600825
826 try
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600827 {
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600828 if (pid)
829 {
830 auto n = dataIface.getProcessName(*pid);
831 if (n)
832 {
833 name = *n;
834 }
835 }
836 }
Patrick Williams66491c62021-10-06 12:23:37 -0500837 catch (const std::exception& e)
Patrick Williams2544b412022-10-04 08:41:06 -0500838 {}
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600839
Sumit Kumar3160a542021-04-26 08:07:04 -0500840 if (pid)
841 {
842 json["Process Name"] = std::move(name);
843 }
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600844}
845
Matt Spinler677381b2020-01-23 10:04:29 -0600846void addBMCFWVersionIDToJSON(nlohmann::json& json,
847 const DataInterfaceBase& dataIface)
848{
849 auto id = dataIface.getBMCFWVersionID();
850 if (id.empty())
851 {
852 id = unknownValue;
853 }
854
Matt Spinlerc2b8a512021-05-21 12:44:42 -0600855 json["FW Version ID"] = std::move(id);
Matt Spinler677381b2020-01-23 10:04:29 -0600856}
857
Matt Spinler4aa23a12020-02-03 15:05:09 -0600858std::string lastSegment(char separator, std::string data)
859{
860 auto pos = data.find_last_of(separator);
861 if (pos != std::string::npos)
862 {
863 data = data.substr(pos + 1);
864 }
865
866 return data;
867}
868
Ben Tynere32b7e72021-05-18 12:38:40 -0500869void addIMKeyword(nlohmann::json& json, const DataInterfaceBase& dataIface)
870{
871 auto keyword = dataIface.getSystemIMKeyword();
872
873 std::string value{};
874
875 std::for_each(keyword.begin(), keyword.end(), [&](const auto& byte) {
Jayanth Othayoth1aa90d42023-09-13 04:25:45 -0500876 value += std::format("{:02X}", byte);
Ben Tynere32b7e72021-05-18 12:38:40 -0500877 });
878
879 json["System IM"] = value;
880}
881
Matt Spinler4aa23a12020-02-03 15:05:09 -0600882void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface)
883{
884 json["BMCState"] = lastSegment('.', dataIface.getBMCState());
885 json["ChassisState"] = lastSegment('.', dataIface.getChassisState());
886 json["HostState"] = lastSegment('.', dataIface.getHostState());
Sumit Kumar2c36fdd2021-09-21 03:12:11 -0500887 json["BootState"] = lastSegment('.', dataIface.getBootState());
Matt Spinler4aa23a12020-02-03 15:05:09 -0600888}
889
George Liu9ac0d9b2022-07-15 10:57:38 +0800890void addBMCUptime(nlohmann::json& json, const DataInterfaceBase& dataIface)
891{
892 auto seconds = dataIface.getUptimeInSeconds();
893 if (seconds)
894 {
895 json["BMCUptime"] = dataIface.getBMCUptime(*seconds);
896 }
897 else
898 {
899 json["BMCUptime"] = "";
900 }
901 json["BMCLoad"] = dataIface.getBMCLoadAvg();
902}
903
Patrick Williams075c7922024-08-16 15:19:49 -0400904std::unique_ptr<UserData> makeSysInfoUserDataSection(
905 const AdditionalData& ad, const DataInterfaceBase& dataIface,
Arya K Padmand8ae6182024-07-19 06:25:10 -0500906 bool addUptime, const nlohmann::json& adSysInfoData)
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600907{
908 nlohmann::json json;
909
910 addProcessNameToJSON(json, ad.getValue("_PID"), dataIface);
Matt Spinler677381b2020-01-23 10:04:29 -0600911 addBMCFWVersionIDToJSON(json, dataIface);
Ben Tynere32b7e72021-05-18 12:38:40 -0500912 addIMKeyword(json, dataIface);
Matt Spinler4aa23a12020-02-03 15:05:09 -0600913 addStatesToJSON(json, dataIface);
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600914
George Liu9ac0d9b2022-07-15 10:57:38 +0800915 if (addUptime)
916 {
917 addBMCUptime(json, dataIface);
918 }
919
Arya K Padmand8ae6182024-07-19 06:25:10 -0500920 if (!adSysInfoData.empty())
921 {
922 json.update(adSysInfoData);
923 }
924
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600925 return makeJSONUserDataSection(json);
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600926}
927
Matt Spinler5b289b22020-03-26 14:27:19 -0500928std::vector<uint8_t> readFD(int fd)
929{
930 std::vector<uint8_t> data;
931
932 // Get the size
933 struct stat s;
934 int r = fstat(fd, &s);
935 if (r != 0)
936 {
937 auto e = errno;
Matt Spinlerfd2da662023-07-07 16:25:14 -0500938 lg2::error("Could not get FFDC file size from FD, errno = {ERRNO}",
939 "ERRNO", e);
Matt Spinler5b289b22020-03-26 14:27:19 -0500940 return data;
941 }
942
943 if (0 == s.st_size)
944 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500945 lg2::error("FFDC file is empty");
Matt Spinler5b289b22020-03-26 14:27:19 -0500946 return data;
947 }
948
949 data.resize(s.st_size);
950
951 // Make sure its at the beginning, as maybe another
952 // extension already used it.
953 r = lseek(fd, 0, SEEK_SET);
954 if (r == -1)
955 {
956 auto e = errno;
Matt Spinlerfd2da662023-07-07 16:25:14 -0500957 lg2::error("Could not seek to beginning of FFDC file, errno = {ERRNO}",
958 "ERRNO", e);
Matt Spinler5b289b22020-03-26 14:27:19 -0500959 return data;
960 }
961
962 r = read(fd, data.data(), s.st_size);
963 if (r == -1)
964 {
965 auto e = errno;
Matt Spinlerfd2da662023-07-07 16:25:14 -0500966 lg2::error("Could not read FFDC file, errno = {ERRNO}", "ERRNO", e);
Matt Spinler5b289b22020-03-26 14:27:19 -0500967 }
968 else if (r != s.st_size)
969 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500970 lg2::warning("Could not read full FFDC file. "
971 "File size = {FSIZE}, Size read = {SIZE_READ}",
972 "FSIZE", s.st_size, "SIZE_READ", r);
Matt Spinler5b289b22020-03-26 14:27:19 -0500973 }
974
975 return data;
976}
977
Patrick Williams25291152025-02-01 08:21:42 -0500978std::unique_ptr<UserData> makeFFDCuserDataSection(uint16_t componentID,
979 const PelFFDCfile& file)
Matt Spinler56ad2a02020-03-26 14:00:52 -0500980{
Matt Spinler5b289b22020-03-26 14:27:19 -0500981 auto data = readFD(file.fd);
982
983 if (data.empty())
984 {
985 return std::unique_ptr<UserData>();
986 }
987
988 // The data needs 4 Byte alignment, and save amount padded for the
989 // CBOR case.
990 uint32_t pad = 0;
991 while (data.size() % 4)
992 {
993 data.push_back(0);
994 pad++;
995 }
996
997 // For JSON, CBOR, and Text use our component ID, subType, and version,
998 // otherwise use the supplied ones.
999 uint16_t compID = static_cast<uint16_t>(ComponentID::phosphorLogging);
1000 uint8_t subType{};
1001 uint8_t version{};
1002
1003 switch (file.format)
1004 {
1005 case UserDataFormat::json:
1006 subType = static_cast<uint8_t>(UserDataFormat::json);
1007 version = static_cast<uint8_t>(UserDataFormatVersion::json);
1008 break;
1009 case UserDataFormat::cbor:
1010 subType = static_cast<uint8_t>(UserDataFormat::cbor);
1011 version = static_cast<uint8_t>(UserDataFormatVersion::cbor);
1012
1013 // The CBOR parser will fail on the extra pad bytes since they
1014 // aren't CBOR. Add the amount we padded to the end and other
1015 // code will remove it all before parsing.
1016 {
1017 data.resize(data.size() + 4);
1018 Stream stream{data};
1019 stream.offset(data.size() - 4);
1020 stream << pad;
1021 }
1022
1023 break;
1024 case UserDataFormat::text:
1025 subType = static_cast<uint8_t>(UserDataFormat::text);
1026 version = static_cast<uint8_t>(UserDataFormatVersion::text);
1027 break;
1028 case UserDataFormat::custom:
1029 default:
1030 // Use the passed in values
1031 compID = componentID;
1032 subType = file.subType;
1033 version = file.version;
1034 break;
1035 }
1036
1037 return std::make_unique<UserData>(compID, subType, version, data);
Matt Spinler56ad2a02020-03-26 14:00:52 -05001038}
1039
Matt Spinler9d921092022-12-15 11:54:49 -06001040std::vector<uint8_t> flattenLines(const std::vector<std::string>& lines)
1041{
1042 std::vector<uint8_t> out;
1043
1044 for (const auto& line : lines)
1045 {
1046 out.insert(out.end(), line.begin(), line.end());
1047
1048 if (out.back() != '\n')
1049 {
1050 out.push_back('\n');
1051 }
1052 }
1053
1054 return out;
1055}
1056
Arya K Padmand8ae6182024-07-19 06:25:10 -05001057void addDIMMInfo(const std::string& locationCode,
1058 const std::vector<std::uint8_t>& diPropVal,
1059 nlohmann::json& adSysInfoData)
1060{
1061 nlohmann::json dimmInfoObj;
1062 dimmInfoObj["Location Code"] = locationCode;
1063 std::ranges::transform(
1064 diPropVal, std::back_inserter(dimmInfoObj["DRAM Manufacturer ID"]),
1065 [](const auto& diPropEachByte) {
1066 return std::format("{:#04x}", diPropEachByte);
1067 });
1068 adSysInfoData["DIMMs Additional Info"] += dimmInfoObj;
1069}
1070
Matt Spinlerc7c3e402020-01-22 15:07:25 -06001071} // namespace util
1072
Matt Spinlercb6b0592019-07-16 15:58:51 -05001073} // namespace pels
1074} // namespace openpower