blob: 9f09a01b2f31129478e340dada758c7811578ee5 [file] [log] [blame]
Matt Spinler711d51d2019-11-06 09:36:51 -06001/**
2 * Copyright © 2019 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Jayanth Othayothda9b5832021-11-05 04:19:43 -050016#include "config.h"
17
Matt Spinlercb6b0592019-07-16 15:58:51 -050018#include "pel.hpp"
19
20#include "bcd_time.hpp"
Matt Spinler386a61e2020-08-13 15:51:12 -050021#include "extended_user_data.hpp"
Matt Spinlerc63e2e82019-12-02 15:50:12 -060022#include "extended_user_header.hpp"
Matt Spinleraa659472019-10-23 09:26:48 -050023#include "failing_mtms.hpp"
Andrew Geisslerf8e750d2022-01-14 14:56:13 -060024#include "fru_identity.hpp"
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +080025#include "json_utils.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050026#include "log_id.hpp"
Matt Spinlerf1e85e22019-11-01 11:31:31 -050027#include "pel_rules.hpp"
Aatir186ce8c2019-10-20 15:13:39 -050028#include "pel_values.hpp"
Matt Spinler131870c2019-09-25 13:29:04 -050029#include "section_factory.hpp"
Matt Spinlerbd716f02019-10-15 10:54:11 -050030#include "src.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050031#include "stream.hpp"
Matt Spinlerafa857c2019-10-24 13:03:46 -050032#include "user_data_formats.hpp"
Matt Spinlercb6b0592019-07-16 15:58:51 -050033
Jayanth Othayoth92b20662021-11-05 00:09:15 -050034#ifdef PEL_ENABLE_PHAL
Jayanth Othayothda9b5832021-11-05 04:19:43 -050035#include "phal_service_actions.hpp"
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050036#include "sbe_ffdc_handler.hpp"
Deepa Karthikeyanff35be32024-10-15 09:10:49 -050037
38#include <libguard/guard_interface.hpp>
39#include <libguard/include/guard_record.hpp>
40
41namespace libguard = openpower::guard;
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050042#endif
43
Matt Spinler5b289b22020-03-26 14:27:19 -050044#include <sys/stat.h>
45#include <unistd.h>
46
Matt Spinlerfd2da662023-07-07 16:25:14 -050047#include <phosphor-logging/lg2.hpp>
Matt Spinler07eefc52019-09-26 11:18:26 -050048
Jayanth Othayoth1aa90d42023-09-13 04:25:45 -050049#include <format>
Patrick Williams2544b412022-10-04 08:41:06 -050050#include <iostream>
Arya K Padmand8ae6182024-07-19 06:25:10 -050051#include <ranges>
Patrick Williams2544b412022-10-04 08:41:06 -050052
Matt Spinlercb6b0592019-07-16 15:58:51 -050053namespace openpower
54{
55namespace pels
56{
Aatir186ce8c2019-10-20 15:13:39 -050057namespace pv = openpower::pels::pel_values;
Matt Spinlerb8323632019-09-20 15:11:04 -050058
Matt Spinler677381b2020-01-23 10:04:29 -060059constexpr auto unknownValue = "Unknown";
Arya K Padmand8ae6182024-07-19 06:25:10 -050060constexpr auto AdDIMMInfoFetchError = "DIMMs Info Fetch Error";
Matt Spinler677381b2020-01-23 10:04:29 -060061
Matt Spinler4bfc9082020-03-24 15:05:54 -050062PEL::PEL(const message::Entry& regEntry, uint32_t obmcLogID, uint64_t timestamp,
Matt Spinlerbd716f02019-10-15 10:54:11 -050063 phosphor::logging::Entry::Level severity,
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050064 const AdditionalData& additionalData, const PelFFDC& ffdcFilesIn,
Matt Spinler9d921092022-12-15 11:54:49 -060065 const DataInterfaceBase& dataIface, const JournalBase& journal)
Matt Spinlerb8323632019-09-20 15:11:04 -050066{
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050067 // No changes in input, for non SBE error related requests
68 PelFFDC ffdcFiles = ffdcFilesIn;
69
Jayanth Othayoth92b20662021-11-05 00:09:15 -050070#ifdef PEL_ENABLE_PHAL
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050071 // Add sbe ffdc processed data into ffdcfiles.
72 namespace sbe = openpower::pels::sbe;
Patrick Williams075c7922024-08-16 15:19:49 -040073 auto processReq =
74 std::any_of(ffdcFiles.begin(), ffdcFiles.end(), [](const auto& file) {
75 return file.format == UserDataFormat::custom &&
76 file.subType == sbe::sbeFFDCSubType;
77 });
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050078 // sbeFFDC can't be destroyed until the end of the PEL constructor
79 // because it needs to keep around the FFDC Files to be used below.
80 std::unique_ptr<sbe::SbeFFDC> sbeFFDCPtr;
81 if (processReq)
82 {
Patrick Williams075c7922024-08-16 15:19:49 -040083 sbeFFDCPtr =
84 std::make_unique<sbe::SbeFFDC>(additionalData, ffdcFilesIn);
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050085 const auto& sbeFFDCFiles = sbeFFDCPtr->getSbeFFDC();
86 ffdcFiles.insert(ffdcFiles.end(), sbeFFDCFiles.begin(),
87 sbeFFDCFiles.end());
Jayanth Othayoth742b00b2022-06-30 05:16:57 -050088
89 // update pel priority for spare clock failures
90 if (auto customSeverity = sbeFFDCPtr->getSeverity())
91 {
92 severity = customSeverity.value();
93 }
Jayanth Othayothe8bdeea2021-06-03 03:01:16 -050094 }
95#endif
96
Arya K Padmand8ae6182024-07-19 06:25:10 -050097 DebugData debugData;
Matt Spinler5a90a952020-08-27 09:39:03 -050098 nlohmann::json callouts;
Matt Spinler85f61a62020-06-03 16:28:55 -050099
Matt Spinler4bfc9082020-03-24 15:05:54 -0500100 _ph = std::make_unique<PrivateHeader>(regEntry.componentID, obmcLogID,
Matt Spinlerb8323632019-09-20 15:11:04 -0500101 timestamp);
Vijay Lobo6b3f3452021-04-15 23:04:42 -0500102 _uh = std::make_unique<UserHeader>(regEntry, severity, additionalData,
103 dataIface);
Matt Spinlerb8323632019-09-20 15:11:04 -0500104
Matt Spinler5a90a952020-08-27 09:39:03 -0500105 // Extract any callouts embedded in an FFDC file.
106 if (!ffdcFiles.empty())
107 {
108 try
109 {
110 callouts = getCalloutJSON(ffdcFiles);
111 }
112 catch (const std::exception& e)
113 {
114 debugData.emplace("FFDC file JSON callouts error",
115 std::vector<std::string>{e.what()});
116 }
117 }
118
Patrick Williams075c7922024-08-16 15:19:49 -0400119 auto src =
120 std::make_unique<SRC>(regEntry, additionalData, callouts, dataIface);
Matt Spinler5a90a952020-08-27 09:39:03 -0500121
Arya K Padmand8ae6182024-07-19 06:25:10 -0500122 nlohmann::json adSysInfoData(nlohmann::json::value_t::object);
123 addAdDetailsForDIMMsCallout(src, dataIface, adSysInfoData, debugData);
124
Matt Spinler85f61a62020-06-03 16:28:55 -0500125 if (!src->getDebugData().empty())
126 {
127 // Something didn't go as planned
128 debugData.emplace("SRC", src->getDebugData());
129 }
Matt Spinlerc63e2e82019-12-02 15:50:12 -0600130
Matt Spinler4bfc9082020-03-24 15:05:54 -0500131 auto euh = std::make_unique<ExtendedUserHeader>(dataIface, regEntry, *src);
Matt Spinlerc63e2e82019-12-02 15:50:12 -0600132
Matt Spinlerbd716f02019-10-15 10:54:11 -0500133 _optionalSections.push_back(std::move(src));
Matt Spinlerc63e2e82019-12-02 15:50:12 -0600134 _optionalSections.push_back(std::move(euh));
Matt Spinlerb8323632019-09-20 15:11:04 -0500135
Matt Spinleraa659472019-10-23 09:26:48 -0500136 auto mtms = std::make_unique<FailingMTMS>(dataIface);
137 _optionalSections.push_back(std::move(mtms));
Matt Spinlerbd716f02019-10-15 10:54:11 -0500138
Arya K Padmand8ae6182024-07-19 06:25:10 -0500139 auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface, true,
140 adSysInfoData);
Matt Spinler85f61a62020-06-03 16:28:55 -0500141 addUserDataSection(std::move(ud));
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600142
Sumit Kumar3e274432021-09-14 06:37:56 -0500143 // Check for pel severity of type - 0x51 = critical error, system
144 // termination and update terminate bit in SRC for pels
145 updateTerminateBitInSRCSection();
146
Matt Spinler9b7e94f2020-03-24 15:44:41 -0500147 // Create a UserData section from AdditionalData.
Matt Spinlerafa857c2019-10-24 13:03:46 -0500148 if (!additionalData.empty())
149 {
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600150 ud = util::makeADUserDataSection(additionalData);
Matt Spinler85f61a62020-06-03 16:28:55 -0500151 addUserDataSection(std::move(ud));
Matt Spinlerafa857c2019-10-24 13:03:46 -0500152 }
153
Matt Spinler56ad2a02020-03-26 14:00:52 -0500154 // Add any FFDC files into UserData sections
155 for (const auto& file : ffdcFiles)
156 {
157 ud = util::makeFFDCuserDataSection(regEntry.componentID, file);
158 if (!ud)
159 {
Matt Spinler85f61a62020-06-03 16:28:55 -0500160 // Add this error into the debug data UserData section
161 std::ostringstream msg;
162 msg << "Could not make PEL FFDC UserData section from file"
163 << std::hex << regEntry.componentID << " " << file.subType
164 << " " << file.version;
165 if (debugData.count("FFDC File"))
166 {
167 debugData.at("FFDC File").push_back(msg.str());
168 }
169 else
170 {
171 debugData.emplace("FFDC File",
172 std::vector<std::string>{msg.str()});
173 }
174
Matt Spinler56ad2a02020-03-26 14:00:52 -0500175 continue;
176 }
177
Matt Spinler85f61a62020-06-03 16:28:55 -0500178 addUserDataSection(std::move(ud));
179 }
Matt Spinler56ad2a02020-03-26 14:00:52 -0500180
Jayanth Othayothda9b5832021-11-05 04:19:43 -0500181#ifdef PEL_ENABLE_PHAL
182 auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID);
Deepa Karthikeyanff35be32024-10-15 09:10:49 -0500183 openpower::pels::phal::createServiceActions(callouts, dataIface, plid());
Jayanth Othayothda9b5832021-11-05 04:19:43 -0500184#endif
185
Matt Spinler85f61a62020-06-03 16:28:55 -0500186 // Store in the PEL any important debug data created while
187 // building the PEL sections.
188 if (!debugData.empty())
189 {
190 nlohmann::json data;
191 data["PEL Internal Debug Data"] = debugData;
192 ud = util::makeJSONUserDataSection(data);
193
194 addUserDataSection(std::move(ud));
195
196 // Also put in the journal for debug
Matt Spinler45796e82022-07-01 11:25:27 -0500197 for (const auto& [name, msgs] : debugData)
Matt Spinler85f61a62020-06-03 16:28:55 -0500198 {
Matt Spinler45796e82022-07-01 11:25:27 -0500199 for (const auto& message : msgs)
Matt Spinler85f61a62020-06-03 16:28:55 -0500200 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500201 lg2::info("{NAME}: {MSG}", "NAME", name, "MSG", message);
Matt Spinler56ad2a02020-03-26 14:00:52 -0500202 }
203 }
Matt Spinler56ad2a02020-03-26 14:00:52 -0500204 }
205
Matt Spinler9d921092022-12-15 11:54:49 -0600206 addJournalSections(regEntry, journal);
207
Matt Spinler97d19b42019-10-29 11:34:03 -0500208 _ph->setSectionCount(2 + _optionalSections.size());
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500209
210 checkRulesAndFix();
Matt Spinlerb8323632019-09-20 15:11:04 -0500211}
Matt Spinlercb6b0592019-07-16 15:58:51 -0500212
Patrick Williams2544b412022-10-04 08:41:06 -0500213PEL::PEL(std::vector<uint8_t>& data) : PEL(data, 0) {}
Matt Spinlercb6b0592019-07-16 15:58:51 -0500214
Matt Spinler07eefc52019-09-26 11:18:26 -0500215PEL::PEL(std::vector<uint8_t>& data, uint32_t obmcLogID)
Matt Spinlercb6b0592019-07-16 15:58:51 -0500216{
Matt Spinler07eefc52019-09-26 11:18:26 -0500217 populateFromRawData(data, obmcLogID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500218}
219
Matt Spinler07eefc52019-09-26 11:18:26 -0500220void PEL::populateFromRawData(std::vector<uint8_t>& data, uint32_t obmcLogID)
Matt Spinlercb6b0592019-07-16 15:58:51 -0500221{
Matt Spinler07eefc52019-09-26 11:18:26 -0500222 Stream pelData{data};
Matt Spinlercb6b0592019-07-16 15:58:51 -0500223 _ph = std::make_unique<PrivateHeader>(pelData);
224 if (obmcLogID != 0)
225 {
Matt Spinler97d19b42019-10-29 11:34:03 -0500226 _ph->setOBMCLogID(obmcLogID);
Matt Spinlercb6b0592019-07-16 15:58:51 -0500227 }
228
229 _uh = std::make_unique<UserHeader>(pelData);
Matt Spinler131870c2019-09-25 13:29:04 -0500230
231 // Use the section factory to create the rest of the objects
232 for (size_t i = 2; i < _ph->sectionCount(); i++)
233 {
234 auto section = section_factory::create(pelData);
235 _optionalSections.push_back(std::move(section));
236 }
Matt Spinlercb6b0592019-07-16 15:58:51 -0500237}
238
239bool PEL::valid() const
240{
241 bool valid = _ph->valid();
242
243 if (valid)
244 {
245 valid = _uh->valid();
246 }
247
Matt Spinler131870c2019-09-25 13:29:04 -0500248 if (valid)
249 {
250 if (!std::all_of(_optionalSections.begin(), _optionalSections.end(),
251 [](const auto& section) { return section->valid(); }))
252 {
253 valid = false;
254 }
255 }
256
Matt Spinlercb6b0592019-07-16 15:58:51 -0500257 return valid;
258}
259
260void PEL::setCommitTime()
261{
262 auto now = std::chrono::system_clock::now();
Matt Spinler97d19b42019-10-29 11:34:03 -0500263 _ph->setCommitTimestamp(getBCDTime(now));
Matt Spinlercb6b0592019-07-16 15:58:51 -0500264}
265
266void PEL::assignID()
267{
Matt Spinler97d19b42019-10-29 11:34:03 -0500268 _ph->setID(generatePELID());
Matt Spinlercb6b0592019-07-16 15:58:51 -0500269}
270
Matt Spinler06885452019-11-06 10:35:42 -0600271void PEL::flatten(std::vector<uint8_t>& pelBuffer) const
Matt Spinlercb6b0592019-07-16 15:58:51 -0500272{
273 Stream pelData{pelBuffer};
Matt Spinlerb8323632019-09-20 15:11:04 -0500274
Matt Spinler07eefc52019-09-26 11:18:26 -0500275 if (!valid())
Matt Spinlercb6b0592019-07-16 15:58:51 -0500276 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500277 lg2::warning("Unflattening an invalid PEL");
Matt Spinlercb6b0592019-07-16 15:58:51 -0500278 }
279
Matt Spinler07eefc52019-09-26 11:18:26 -0500280 _ph->flatten(pelData);
Matt Spinlerb8323632019-09-20 15:11:04 -0500281 _uh->flatten(pelData);
Matt Spinler07eefc52019-09-26 11:18:26 -0500282
283 for (auto& section : _optionalSections)
284 {
285 section->flatten(pelData);
286 }
Matt Spinlercb6b0592019-07-16 15:58:51 -0500287}
288
Matt Spinler06885452019-11-06 10:35:42 -0600289std::vector<uint8_t> PEL::data() const
Matt Spinlercb6b0592019-07-16 15:58:51 -0500290{
Matt Spinler07eefc52019-09-26 11:18:26 -0500291 std::vector<uint8_t> pelData;
292 flatten(pelData);
293 return pelData;
Matt Spinlercb6b0592019-07-16 15:58:51 -0500294}
295
Matt Spinlerf1b46ff2020-01-22 14:10:04 -0600296size_t PEL::size() const
297{
298 size_t size = 0;
299
300 if (_ph)
301 {
302 size += _ph->header().size;
303 }
304
305 if (_uh)
306 {
307 size += _uh->header().size;
308 }
309
310 for (const auto& section : _optionalSections)
311 {
312 size += section->header().size;
313 }
314
315 return size;
316}
317
Matt Spinlerbd716f02019-10-15 10:54:11 -0500318std::optional<SRC*> PEL::primarySRC() const
319{
Patrick Williams075c7922024-08-16 15:19:49 -0400320 auto src = std::find_if(
321 _optionalSections.begin(), _optionalSections.end(), [](auto& section) {
322 return section->header().id ==
323 static_cast<uint16_t>(SectionID::primarySRC);
324 });
Matt Spinlerbd716f02019-10-15 10:54:11 -0500325 if (src != _optionalSections.end())
326 {
327 return static_cast<SRC*>(src->get());
328 }
329
330 return std::nullopt;
331}
332
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500333void PEL::checkRulesAndFix()
334{
Matt Spinler1f93c592020-09-10 10:43:08 -0500335 // Only fix if the action flags are at their default value which
336 // means they weren't specified in the registry. Otherwise
337 // assume the user knows what they are doing.
338 if (_uh->actionFlags() == actionFlagsDefault)
339 {
Patrick Williams075c7922024-08-16 15:19:49 -0400340 auto [actionFlags, eventType] =
341 pel_rules::check(0, _uh->eventType(), _uh->severity());
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500342
Matt Spinler1f93c592020-09-10 10:43:08 -0500343 _uh->setActionFlags(actionFlags);
344 _uh->setEventType(eventType);
345 }
Matt Spinlerf1e85e22019-11-01 11:31:31 -0500346}
347
Patrick Williams075c7922024-08-16 15:19:49 -0400348void PEL::printSectionInJSON(
349 const Section& section, std::string& buf,
350 std::map<uint16_t, size_t>& pluralSections, message::Registry& registry,
351 const std::vector<std::string>& plugins, uint8_t creatorID) const
Aatir186ce8c2019-10-20 15:13:39 -0500352{
353 char tmpB[5];
Aatir Manzurad0e0472019-10-07 13:18:37 -0500354 uint8_t id[] = {static_cast<uint8_t>(section.header().id >> 8),
355 static_cast<uint8_t>(section.header().id)};
356 sprintf(tmpB, "%c%c", id[0], id[1]);
357 std::string sectionID(tmpB);
358 std::string sectionName = pv::sectionTitles.count(sectionID)
359 ? pv::sectionTitles.at(sectionID)
360 : "Unknown Section";
Matt Spinleracb7c102020-01-10 13:49:22 -0600361
362 // Add a count if there are multiple of this type of section
363 auto count = pluralSections.find(section.header().id);
364 if (count != pluralSections.end())
365 {
366 sectionName += " " + std::to_string(count->second);
367 count->second++;
368 }
369
Aatir186ce8c2019-10-20 15:13:39 -0500370 if (section.valid())
371 {
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800372 std::optional<std::string> json;
373 if (sectionID == "PS" || sectionID == "SS")
374 {
Harisuddin Mohamed Isac8d6cc62020-08-19 22:47:19 +0800375 json = section.getJSON(registry, plugins, creatorID);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800376 }
Matt Spinler386a61e2020-08-13 15:51:12 -0500377 else if ((sectionID == "UD") || (sectionID == "ED"))
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800378 {
Harisuddin Mohamed Isa3fdcd4e2020-08-26 11:56:42 +0800379 json = section.getJSON(creatorID, plugins);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800380 }
381 else
382 {
Matt Spinlerb832aa52023-03-21 15:32:34 -0500383 json = section.getJSON(creatorID);
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800384 }
Matt Spinler4220a152020-03-26 10:18:09 -0500385
386 buf += "\"" + sectionName + "\": {\n";
387
Aatir Manzurad0e0472019-10-07 13:18:37 -0500388 if (json)
389 {
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800390 buf += *json + "\n},\n";
Aatir Manzurad0e0472019-10-07 13:18:37 -0500391 }
392 else
393 {
Matt Spinler4220a152020-03-26 10:18:09 -0500394 jsonInsert(buf, pv::sectionVer,
395 getNumberString("%d", section.header().version), 1);
396 jsonInsert(buf, pv::subSection,
397 getNumberString("%d", section.header().subType), 1);
398 jsonInsert(buf, pv::createdBy,
399 getNumberString("0x%X", section.header().componentID),
400 1);
401
Aatir Manzurad0e0472019-10-07 13:18:37 -0500402 std::vector<uint8_t> data;
403 Stream s{data};
404 section.flatten(s);
Matt Spinler4220a152020-03-26 10:18:09 -0500405 std::string dstr =
406 dumpHex(std::data(data) + SectionHeader::flattenedSize(),
Arya K Padman8c8aaa02024-04-28 23:23:45 -0500407 data.size() - SectionHeader::flattenedSize(), 2)
408 .get();
Matt Spinler4220a152020-03-26 10:18:09 -0500409 std::string jsonIndent(indentLevel, 0x20);
410 buf += jsonIndent + "\"Data\": [\n";
411 buf += dstr;
412 buf += jsonIndent + "]\n";
413 buf += "},\n";
Aatir Manzurad0e0472019-10-07 13:18:37 -0500414 }
Aatir186ce8c2019-10-20 15:13:39 -0500415 }
416 else
417 {
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800418 buf += "\n\"Invalid Section\": [\n \"invalid\"\n],\n";
Aatir186ce8c2019-10-20 15:13:39 -0500419 }
420}
421
Matt Spinleracb7c102020-01-10 13:49:22 -0600422std::map<uint16_t, size_t> PEL::getPluralSections() const
423{
424 std::map<uint16_t, size_t> sectionCounts;
425
426 for (const auto& section : optionalSections())
427 {
428 if (sectionCounts.find(section->header().id) == sectionCounts.end())
429 {
430 sectionCounts[section->header().id] = 1;
431 }
432 else
433 {
434 sectionCounts[section->header().id]++;
435 }
436 }
437
438 std::map<uint16_t, size_t> sections;
439 for (const auto& [id, count] : sectionCounts)
440 {
441 if (count > 1)
442 {
443 // Start with 0 here and printSectionInJSON()
444 // will increment it as it goes.
445 sections.emplace(id, 0);
446 }
447 }
448
449 return sections;
450}
451
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800452void PEL::toJSON(message::Registry& registry,
453 const std::vector<std::string>& plugins) const
Aatir186ce8c2019-10-20 15:13:39 -0500454{
Matt Spinleracb7c102020-01-10 13:49:22 -0600455 auto sections = getPluralSections();
456
Harisuddin Mohamed Isaa214ed32020-02-28 15:58:23 +0800457 std::string buf = "{\n";
Matt Spinlerb832aa52023-03-21 15:32:34 -0500458 printSectionInJSON(*(_ph.get()), buf, sections, registry, plugins,
459 _ph->creatorID());
460 printSectionInJSON(*(_uh.get()), buf, sections, registry, plugins,
461 _ph->creatorID());
Aatir186ce8c2019-10-20 15:13:39 -0500462 for (auto& section : this->optionalSections())
463 {
Harisuddin Mohamed Isaf67bafd2020-07-06 17:51:21 +0800464 printSectionInJSON(*(section.get()), buf, sections, registry, plugins,
465 _ph->creatorID());
Aatir186ce8c2019-10-20 15:13:39 -0500466 }
467 buf += "}";
468 std::size_t found = buf.rfind(",");
469 if (found != std::string::npos)
470 buf.replace(found, 1, "");
471 std::cout << buf << std::endl;
472}
Harisuddin Mohamed Isa600d15a2019-12-20 12:42:26 +0800473
Matt Spinler85f61a62020-06-03 16:28:55 -0500474bool PEL::addUserDataSection(std::unique_ptr<UserData> userData)
475{
476 if (size() + userData->header().size > _maxPELSize)
477 {
478 if (userData->shrink(_maxPELSize - size()))
479 {
480 _optionalSections.push_back(std::move(userData));
481 }
482 else
483 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500484 lg2::warning("Could not shrink UserData section. Dropping. "
485 "Section size = {SSIZE}, Component ID = {COMP_ID}, "
486 "Subtype = {SUBTYPE}, Version = {VERSION}",
487 "SSIZE", userData->header().size, "COMP_ID",
488 userData->header().componentID, "SUBTYPE",
489 userData->header().subType, "VERSION",
490 userData->header().version);
Matt Spinler85f61a62020-06-03 16:28:55 -0500491 return false;
492 }
493 }
494 else
495 {
496 _optionalSections.push_back(std::move(userData));
497 }
498 return true;
499}
500
Matt Spinler5a90a952020-08-27 09:39:03 -0500501nlohmann::json PEL::getCalloutJSON(const PelFFDC& ffdcFiles)
502{
503 nlohmann::json callouts;
504
505 for (const auto& file : ffdcFiles)
506 {
507 if ((file.format == UserDataFormat::json) &&
508 (file.subType == jsonCalloutSubtype))
509 {
510 auto data = util::readFD(file.fd);
511 if (data.empty())
512 {
513 throw std::runtime_error{
514 "Could not get data from JSON callout file descriptor"};
515 }
516
517 std::string jsonString{data.begin(), data.begin() + data.size()};
518
519 callouts = nlohmann::json::parse(jsonString);
520 break;
521 }
522 }
523
524 return callouts;
525}
526
Andrew Geisslerf8e750d2022-01-14 14:56:13 -0600527bool PEL::isHwCalloutPresent() const
Andrew Geissler44fc3162020-07-09 09:21:31 -0500528{
529 auto pSRC = primarySRC();
530 if (!pSRC)
531 {
532 return false;
533 }
534
535 bool calloutPresent = false;
536 if ((*pSRC)->callouts())
537 {
538 for (auto& i : (*pSRC)->callouts()->callouts())
539 {
540 if (((*i).fruIdentity()))
541 {
542 auto& fruId = (*i).fruIdentity();
Andrew Geisslerf8e750d2022-01-14 14:56:13 -0600543 if ((*fruId).failingComponentType() ==
544 src::FRUIdentity::hardwareFRU)
Andrew Geissler44fc3162020-07-09 09:21:31 -0500545 {
546 calloutPresent = true;
547 break;
548 }
549 }
550 }
551 }
552
553 return calloutPresent;
554}
555
Sumit Kumar3160a542021-04-26 08:07:04 -0500556void PEL::updateSysInfoInExtendedUserDataSection(
557 const DataInterfaceBase& dataIface)
558{
559 const AdditionalData additionalData;
560
561 // Check for PEL from Hostboot
562 if (_ph->creatorID() == static_cast<uint8_t>(CreatorID::hostboot))
563 {
564 // Get the ED section from PEL
Patrick Williams075c7922024-08-16 15:19:49 -0400565 auto op = std::find_if(
566 _optionalSections.begin(), _optionalSections.end(),
567 [](auto& section) {
568 return section->header().id ==
569 static_cast<uint16_t>(SectionID::extUserData);
570 });
Sumit Kumar3160a542021-04-26 08:07:04 -0500571
572 // Check for ED section found and its not the last section of PEL
573 if (op != _optionalSections.end())
574 {
575 // Get the extended user data class mapped to found section
576 auto extUserData = static_cast<ExtendedUserData*>(op->get());
577
578 // Check for the creator ID is for OpenBMC
579 if (extUserData->creatorID() ==
580 static_cast<uint8_t>(CreatorID::openBMC))
581 {
582 // Update subtype and component id
583 auto subType = static_cast<uint8_t>(UserDataFormat::json);
584 auto componentId =
585 static_cast<uint16_t>(ComponentID::phosphorLogging);
586
587 // Update system data to ED section
George Liu9ac0d9b2022-07-15 10:57:38 +0800588 auto ud = util::makeSysInfoUserDataSection(additionalData,
589 dataIface, false);
Sumit Kumar3160a542021-04-26 08:07:04 -0500590 extUserData->updateDataSection(subType, componentId,
591 ud->data());
592 }
593 }
594 }
595}
596
Matt Spinler8e65f4e2023-05-02 13:40:08 -0500597bool PEL::getDeconfigFlag() 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::deconfigured);
605 }
606 return false;
607}
608
609bool PEL::getGuardFlag() const
610{
611 auto creator = static_cast<CreatorID>(_ph->creatorID());
612
613 if ((creator == CreatorID::openBMC) || (creator == CreatorID::hostboot))
614 {
615 auto src = primarySRC();
616 return (*src)->getErrorStatusFlag(SRC::ErrorStatusFlags::guarded);
617 }
618 return false;
619}
620
Sumit Kumar3e274432021-09-14 06:37:56 -0500621void PEL::updateTerminateBitInSRCSection()
622{
623 // Check for pel severity of type - 0x51 = critical error, system
624 // termination
625 if (_uh->severity() == 0x51)
626 {
627 // Get the primary SRC section
628 auto pSRC = primarySRC();
629 if (pSRC)
630 {
631 (*pSRC)->setTerminateBit();
632 }
633 }
634}
635
Matt Spinler9d921092022-12-15 11:54:49 -0600636void PEL::addJournalSections(const message::Entry& regEntry,
637 const JournalBase& journal)
638{
639 if (!regEntry.journalCapture)
640 {
641 return;
642 }
643
644 // Write all unwritten journal data to disk.
645 journal.sync();
646
647 const auto& jc = regEntry.journalCapture.value();
648 std::vector<std::vector<std::string>> allMessages;
649
650 if (std::holds_alternative<size_t>(jc))
651 {
652 // Get the previous numLines journal entries
653 const auto& numLines = std::get<size_t>(jc);
654 try
655 {
656 auto messages = journal.getMessages("", numLines);
657 if (!messages.empty())
658 {
659 allMessages.push_back(std::move(messages));
660 }
661 }
662 catch (const std::exception& e)
663 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500664 lg2::error("Failed during journal collection: {ERROR}", "ERROR", e);
Matt Spinler9d921092022-12-15 11:54:49 -0600665 }
666 }
667 else if (std::holds_alternative<message::AppCaptureList>(jc))
668 {
669 // Get journal entries based on the syslog id field.
670 const auto& sections = std::get<message::AppCaptureList>(jc);
671 for (const auto& [syslogID, numLines] : sections)
672 {
673 try
674 {
675 auto messages = journal.getMessages(syslogID, numLines);
676 if (!messages.empty())
677 {
678 allMessages.push_back(std::move(messages));
679 }
680 }
681 catch (const std::exception& e)
682 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500683 lg2::error("Failed during journal collection: {ERROR}", "ERROR",
684 e);
Matt Spinler9d921092022-12-15 11:54:49 -0600685 }
686 }
687 }
688
689 // Create the UserData sections
690 for (const auto& messages : allMessages)
691 {
692 auto buffer = util::flattenLines(messages);
693
694 // If the buffer is way too big, it can overflow the uint16_t
695 // PEL section size field that is checked below so do a cursory
696 // check here.
697 if (buffer.size() > _maxPELSize)
698 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500699 lg2::warning(
700 "Journal UserData section does not fit in PEL, dropping. "
701 "PEL size = {PEL_SIZE}, data size = {DATA_SIZE}",
702 "PEL_SIZE", size(), "DATA_SIZE", buffer.size());
Matt Spinler9d921092022-12-15 11:54:49 -0600703 continue;
704 }
705
706 // Sections must be 4 byte aligned.
707 while (buffer.size() % 4 != 0)
708 {
709 buffer.push_back(0);
710 }
711
712 auto ud = std::make_unique<UserData>(
713 static_cast<uint16_t>(ComponentID::phosphorLogging),
714 static_cast<uint8_t>(UserDataFormat::text),
715 static_cast<uint8_t>(UserDataFormatVersion::text), buffer);
716
717 if (size() + ud->header().size <= _maxPELSize)
718 {
719 _optionalSections.push_back(std::move(ud));
720 }
721 else
722 {
723 // Don't attempt to shrink here since we'd be dropping the
724 // most recent journal entries which would be confusing.
Matt Spinlerfd2da662023-07-07 16:25:14 -0500725 lg2::warning(
726 "Journal UserData section does not fit in PEL, dropping. "
727 "PEL size = {PEL_SIZE}, data size = {DATA_SIZE}",
728 "PEL_SIZE", size(), "DATA_SIZE", buffer.size());
Matt Spinler9d921092022-12-15 11:54:49 -0600729 ud.reset();
730 continue;
731 }
732 }
733}
734
Arya K Padmand8ae6182024-07-19 06:25:10 -0500735void PEL::addAdDetailsForDIMMsCallout(
736 const std::unique_ptr<SRC>& src, const DataInterfaceBase& dataIface,
737 nlohmann::json& adSysInfoData, DebugData& debugData)
738{
739 if (!src->callouts())
740 {
741 // No callouts
742 return;
743 }
744
745 auto isDIMMCallout = [&dataIface, &debugData](const auto& callout) {
746 auto locCode{callout->locationCode()};
747 if (locCode.empty())
748 {
749 // Not a hardware callout. No action required
750 return false;
751 }
752 else
753 {
Arya K Padmanced8ed72024-09-02 05:18:07 -0500754 return const_cast<DataInterfaceBase&>(dataIface).isDIMM(locCode);
Arya K Padmand8ae6182024-07-19 06:25:10 -0500755 }
756 };
757 auto addAdDIMMDetails = [&dataIface, &adSysInfoData,
758 &debugData](const auto& callout) {
759 auto dimmLocCode{callout->locationCode()};
760
761 auto diPropVal = dataIface.getDIProperty(dimmLocCode);
762 if (!diPropVal.has_value())
763 {
764 std::string errMsg{
765 std::format("Failed reading DI property from "
766 "VINI Interface for the LocationCode:[{}]",
767 dimmLocCode)};
768 debugData[AdDIMMInfoFetchError].emplace_back(errMsg);
769 }
770 else
771 {
772 util::addDIMMInfo(dimmLocCode, diPropVal.value(), adSysInfoData);
773 }
774 };
775
776 auto DIMMsCallouts = src->callouts()->callouts() |
777 std::views::filter(isDIMMCallout);
778
779 std::ranges::for_each(DIMMsCallouts, addAdDIMMDetails);
780}
781
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600782namespace util
783{
784
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600785std::unique_ptr<UserData> makeJSONUserDataSection(const nlohmann::json& json)
786{
Matt Spinler13db1d32024-10-08 11:22:54 -0500787 std::string jsonString;
788 try
789 {
790 jsonString = json.dump();
791 }
792 catch (const std::exception& e)
793 {
794 lg2::error("json.dump() failed with: {ERROR}", "ERROR", e);
795 jsonString = "Invalid JSON passed to makeJSONUserDataSection!";
796 }
797
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600798 std::vector<uint8_t> jsonData(jsonString.begin(), jsonString.end());
799
800 // Pad to a 4 byte boundary
801 while ((jsonData.size() % 4) != 0)
802 {
803 jsonData.push_back(0);
804 }
805
806 return std::make_unique<UserData>(
807 static_cast<uint16_t>(ComponentID::phosphorLogging),
808 static_cast<uint8_t>(UserDataFormat::json),
809 static_cast<uint8_t>(UserDataFormatVersion::json), jsonData);
810}
811
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600812std::unique_ptr<UserData> makeADUserDataSection(const AdditionalData& ad)
813{
814 assert(!ad.empty());
815 nlohmann::json json;
816
817 // Remove the 'ESEL' entry, as it contains a full PEL in the value.
818 if (ad.getValue("ESEL"))
819 {
820 auto newAD = ad;
821 newAD.remove("ESEL");
822 json = newAD.toJSON();
823 }
824 else
825 {
826 json = ad.toJSON();
827 }
828
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600829 return makeJSONUserDataSection(json);
830}
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600831
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600832void addProcessNameToJSON(nlohmann::json& json,
833 const std::optional<std::string>& pid,
834 const DataInterfaceBase& dataIface)
835{
Matt Spinler677381b2020-01-23 10:04:29 -0600836 std::string name{unknownValue};
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600837
838 try
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600839 {
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600840 if (pid)
841 {
842 auto n = dataIface.getProcessName(*pid);
843 if (n)
844 {
845 name = *n;
846 }
847 }
848 }
Patrick Williams66491c62021-10-06 12:23:37 -0500849 catch (const std::exception& e)
Patrick Williams2544b412022-10-04 08:41:06 -0500850 {}
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600851
Sumit Kumar3160a542021-04-26 08:07:04 -0500852 if (pid)
853 {
854 json["Process Name"] = std::move(name);
855 }
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600856}
857
Matt Spinler677381b2020-01-23 10:04:29 -0600858void addBMCFWVersionIDToJSON(nlohmann::json& json,
859 const DataInterfaceBase& dataIface)
860{
861 auto id = dataIface.getBMCFWVersionID();
862 if (id.empty())
863 {
864 id = unknownValue;
865 }
866
Matt Spinlerc2b8a512021-05-21 12:44:42 -0600867 json["FW Version ID"] = std::move(id);
Matt Spinler677381b2020-01-23 10:04:29 -0600868}
869
Matt Spinler4aa23a12020-02-03 15:05:09 -0600870std::string lastSegment(char separator, std::string data)
871{
872 auto pos = data.find_last_of(separator);
873 if (pos != std::string::npos)
874 {
875 data = data.substr(pos + 1);
876 }
877
878 return data;
879}
880
Ben Tynere32b7e72021-05-18 12:38:40 -0500881void addIMKeyword(nlohmann::json& json, const DataInterfaceBase& dataIface)
882{
883 auto keyword = dataIface.getSystemIMKeyword();
884
885 std::string value{};
886
887 std::for_each(keyword.begin(), keyword.end(), [&](const auto& byte) {
Jayanth Othayoth1aa90d42023-09-13 04:25:45 -0500888 value += std::format("{:02X}", byte);
Ben Tynere32b7e72021-05-18 12:38:40 -0500889 });
890
891 json["System IM"] = value;
892}
893
Matt Spinler4aa23a12020-02-03 15:05:09 -0600894void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface)
895{
896 json["BMCState"] = lastSegment('.', dataIface.getBMCState());
897 json["ChassisState"] = lastSegment('.', dataIface.getChassisState());
898 json["HostState"] = lastSegment('.', dataIface.getHostState());
Sumit Kumar2c36fdd2021-09-21 03:12:11 -0500899 json["BootState"] = lastSegment('.', dataIface.getBootState());
Matt Spinler4aa23a12020-02-03 15:05:09 -0600900}
901
George Liu9ac0d9b2022-07-15 10:57:38 +0800902void addBMCUptime(nlohmann::json& json, const DataInterfaceBase& dataIface)
903{
904 auto seconds = dataIface.getUptimeInSeconds();
905 if (seconds)
906 {
907 json["BMCUptime"] = dataIface.getBMCUptime(*seconds);
908 }
909 else
910 {
911 json["BMCUptime"] = "";
912 }
913 json["BMCLoad"] = dataIface.getBMCLoadAvg();
914}
915
Patrick Williams075c7922024-08-16 15:19:49 -0400916std::unique_ptr<UserData> makeSysInfoUserDataSection(
917 const AdditionalData& ad, const DataInterfaceBase& dataIface,
Arya K Padmand8ae6182024-07-19 06:25:10 -0500918 bool addUptime, const nlohmann::json& adSysInfoData)
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600919{
920 nlohmann::json json;
921
922 addProcessNameToJSON(json, ad.getValue("_PID"), dataIface);
Matt Spinler677381b2020-01-23 10:04:29 -0600923 addBMCFWVersionIDToJSON(json, dataIface);
Ben Tynere32b7e72021-05-18 12:38:40 -0500924 addIMKeyword(json, dataIface);
Matt Spinler4aa23a12020-02-03 15:05:09 -0600925 addStatesToJSON(json, dataIface);
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600926
George Liu9ac0d9b2022-07-15 10:57:38 +0800927 if (addUptime)
928 {
929 addBMCUptime(json, dataIface);
930 }
931
Arya K Padmand8ae6182024-07-19 06:25:10 -0500932 if (!adSysInfoData.empty())
933 {
934 json.update(adSysInfoData);
935 }
936
Matt Spinler4dcd3f42020-01-22 14:55:07 -0600937 return makeJSONUserDataSection(json);
Matt Spinlerc7c3e402020-01-22 15:07:25 -0600938}
939
Matt Spinler5b289b22020-03-26 14:27:19 -0500940std::vector<uint8_t> readFD(int fd)
941{
942 std::vector<uint8_t> data;
943
944 // Get the size
945 struct stat s;
946 int r = fstat(fd, &s);
947 if (r != 0)
948 {
949 auto e = errno;
Matt Spinlerfd2da662023-07-07 16:25:14 -0500950 lg2::error("Could not get FFDC file size from FD, errno = {ERRNO}",
951 "ERRNO", e);
Matt Spinler5b289b22020-03-26 14:27:19 -0500952 return data;
953 }
954
955 if (0 == s.st_size)
956 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500957 lg2::error("FFDC file is empty");
Matt Spinler5b289b22020-03-26 14:27:19 -0500958 return data;
959 }
960
961 data.resize(s.st_size);
962
963 // Make sure its at the beginning, as maybe another
964 // extension already used it.
965 r = lseek(fd, 0, SEEK_SET);
966 if (r == -1)
967 {
968 auto e = errno;
Matt Spinlerfd2da662023-07-07 16:25:14 -0500969 lg2::error("Could not seek to beginning of FFDC file, errno = {ERRNO}",
970 "ERRNO", e);
Matt Spinler5b289b22020-03-26 14:27:19 -0500971 return data;
972 }
973
974 r = read(fd, data.data(), s.st_size);
975 if (r == -1)
976 {
977 auto e = errno;
Matt Spinlerfd2da662023-07-07 16:25:14 -0500978 lg2::error("Could not read FFDC file, errno = {ERRNO}", "ERRNO", e);
Matt Spinler5b289b22020-03-26 14:27:19 -0500979 }
980 else if (r != s.st_size)
981 {
Matt Spinlerfd2da662023-07-07 16:25:14 -0500982 lg2::warning("Could not read full FFDC file. "
983 "File size = {FSIZE}, Size read = {SIZE_READ}",
984 "FSIZE", s.st_size, "SIZE_READ", r);
Matt Spinler5b289b22020-03-26 14:27:19 -0500985 }
986
987 return data;
988}
989
Patrick Williams075c7922024-08-16 15:19:49 -0400990std::unique_ptr<UserData>
991 makeFFDCuserDataSection(uint16_t componentID, const PelFFDCfile& file)
Matt Spinler56ad2a02020-03-26 14:00:52 -0500992{
Matt Spinler5b289b22020-03-26 14:27:19 -0500993 auto data = readFD(file.fd);
994
995 if (data.empty())
996 {
997 return std::unique_ptr<UserData>();
998 }
999
1000 // The data needs 4 Byte alignment, and save amount padded for the
1001 // CBOR case.
1002 uint32_t pad = 0;
1003 while (data.size() % 4)
1004 {
1005 data.push_back(0);
1006 pad++;
1007 }
1008
1009 // For JSON, CBOR, and Text use our component ID, subType, and version,
1010 // otherwise use the supplied ones.
1011 uint16_t compID = static_cast<uint16_t>(ComponentID::phosphorLogging);
1012 uint8_t subType{};
1013 uint8_t version{};
1014
1015 switch (file.format)
1016 {
1017 case UserDataFormat::json:
1018 subType = static_cast<uint8_t>(UserDataFormat::json);
1019 version = static_cast<uint8_t>(UserDataFormatVersion::json);
1020 break;
1021 case UserDataFormat::cbor:
1022 subType = static_cast<uint8_t>(UserDataFormat::cbor);
1023 version = static_cast<uint8_t>(UserDataFormatVersion::cbor);
1024
1025 // The CBOR parser will fail on the extra pad bytes since they
1026 // aren't CBOR. Add the amount we padded to the end and other
1027 // code will remove it all before parsing.
1028 {
1029 data.resize(data.size() + 4);
1030 Stream stream{data};
1031 stream.offset(data.size() - 4);
1032 stream << pad;
1033 }
1034
1035 break;
1036 case UserDataFormat::text:
1037 subType = static_cast<uint8_t>(UserDataFormat::text);
1038 version = static_cast<uint8_t>(UserDataFormatVersion::text);
1039 break;
1040 case UserDataFormat::custom:
1041 default:
1042 // Use the passed in values
1043 compID = componentID;
1044 subType = file.subType;
1045 version = file.version;
1046 break;
1047 }
1048
1049 return std::make_unique<UserData>(compID, subType, version, data);
Matt Spinler56ad2a02020-03-26 14:00:52 -05001050}
1051
Matt Spinler9d921092022-12-15 11:54:49 -06001052std::vector<uint8_t> flattenLines(const std::vector<std::string>& lines)
1053{
1054 std::vector<uint8_t> out;
1055
1056 for (const auto& line : lines)
1057 {
1058 out.insert(out.end(), line.begin(), line.end());
1059
1060 if (out.back() != '\n')
1061 {
1062 out.push_back('\n');
1063 }
1064 }
1065
1066 return out;
1067}
1068
Arya K Padmand8ae6182024-07-19 06:25:10 -05001069void addDIMMInfo(const std::string& locationCode,
1070 const std::vector<std::uint8_t>& diPropVal,
1071 nlohmann::json& adSysInfoData)
1072{
1073 nlohmann::json dimmInfoObj;
1074 dimmInfoObj["Location Code"] = locationCode;
1075 std::ranges::transform(
1076 diPropVal, std::back_inserter(dimmInfoObj["DRAM Manufacturer ID"]),
1077 [](const auto& diPropEachByte) {
1078 return std::format("{:#04x}", diPropEachByte);
1079 });
1080 adSysInfoData["DIMMs Additional Info"] += dimmInfoObj;
1081}
1082
Matt Spinlerc7c3e402020-01-22 15:07:25 -06001083} // namespace util
1084
Matt Spinlercb6b0592019-07-16 15:58:51 -05001085} // namespace pels
1086} // namespace openpower