xref: /openbmc/phosphor-logging/extensions/openpower-pels/pel.cpp (revision f8e750dd9ae52480f44f00b32d96af1fae0e3a09)
1 /**
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  */
16 #include "config.h"
17 
18 #include "pel.hpp"
19 
20 #include "bcd_time.hpp"
21 #include "extended_user_data.hpp"
22 #include "extended_user_header.hpp"
23 #include "failing_mtms.hpp"
24 #include "fru_identity.hpp"
25 #include "json_utils.hpp"
26 #include "log_id.hpp"
27 #include "pel_rules.hpp"
28 #include "pel_values.hpp"
29 #include "section_factory.hpp"
30 #include "src.hpp"
31 #include "stream.hpp"
32 #include "user_data_formats.hpp"
33 
34 #ifdef PEL_ENABLE_PHAL
35 #include "phal_service_actions.hpp"
36 #include "sbe_ffdc_handler.hpp"
37 #endif
38 
39 #include <fmt/format.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 
43 #include <iostream>
44 #include <phosphor-logging/log.hpp>
45 
46 namespace openpower
47 {
48 namespace pels
49 {
50 namespace message = openpower::pels::message;
51 namespace pv = openpower::pels::pel_values;
52 using namespace phosphor::logging;
53 
54 constexpr auto unknownValue = "Unknown";
55 
56 PEL::PEL(const message::Entry& regEntry, uint32_t obmcLogID, uint64_t timestamp,
57          phosphor::logging::Entry::Level severity,
58          const AdditionalData& additionalData, const PelFFDC& ffdcFilesIn,
59          const DataInterfaceBase& dataIface)
60 {
61     // No changes in input, for non SBE error related requests
62     PelFFDC ffdcFiles = ffdcFilesIn;
63 
64 #ifdef PEL_ENABLE_PHAL
65     // Add sbe ffdc processed data into ffdcfiles.
66     namespace sbe = openpower::pels::sbe;
67     auto processReq =
68         std::any_of(ffdcFiles.begin(), ffdcFiles.end(), [](const auto& file) {
69             return file.format == UserDataFormat::custom &&
70                    file.subType == sbe::sbeFFDCSubType;
71         });
72     // sbeFFDC can't be destroyed until the end of the PEL constructor
73     // because it needs to keep around the FFDC Files to be used below.
74     std::unique_ptr<sbe::SbeFFDC> sbeFFDCPtr;
75     if (processReq)
76     {
77         sbeFFDCPtr =
78             std::make_unique<sbe::SbeFFDC>(additionalData, ffdcFilesIn);
79         const auto& sbeFFDCFiles = sbeFFDCPtr->getSbeFFDC();
80         ffdcFiles.insert(ffdcFiles.end(), sbeFFDCFiles.begin(),
81                          sbeFFDCFiles.end());
82     }
83 #endif
84 
85     std::map<std::string, std::vector<std::string>> debugData;
86     nlohmann::json callouts;
87 
88     _ph = std::make_unique<PrivateHeader>(regEntry.componentID, obmcLogID,
89                                           timestamp);
90     _uh = std::make_unique<UserHeader>(regEntry, severity, additionalData,
91                                        dataIface);
92 
93     // 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 
107     auto src =
108         std::make_unique<SRC>(regEntry, additionalData, callouts, dataIface);
109 
110     if (!src->getDebugData().empty())
111     {
112         // Something didn't go as planned
113         debugData.emplace("SRC", src->getDebugData());
114     }
115 
116     auto euh = std::make_unique<ExtendedUserHeader>(dataIface, regEntry, *src);
117 
118     _optionalSections.push_back(std::move(src));
119     _optionalSections.push_back(std::move(euh));
120 
121     auto mtms = std::make_unique<FailingMTMS>(dataIface);
122     _optionalSections.push_back(std::move(mtms));
123 
124     auto ud = util::makeSysInfoUserDataSection(additionalData, dataIface);
125     addUserDataSection(std::move(ud));
126 
127     //  Check for pel severity of type - 0x51 = critical error, system
128     //  termination and update terminate bit in SRC for pels
129     updateTerminateBitInSRCSection();
130 
131     // Create a UserData section from AdditionalData.
132     if (!additionalData.empty())
133     {
134         ud = util::makeADUserDataSection(additionalData);
135         addUserDataSection(std::move(ud));
136     }
137 
138     // Add any FFDC files into UserData sections
139     for (const auto& file : ffdcFiles)
140     {
141         ud = util::makeFFDCuserDataSection(regEntry.componentID, file);
142         if (!ud)
143         {
144             // Add this error into the debug data UserData section
145             std::ostringstream msg;
146             msg << "Could not make PEL FFDC UserData section from file"
147                 << std::hex << regEntry.componentID << " " << file.subType
148                 << " " << file.version;
149             if (debugData.count("FFDC File"))
150             {
151                 debugData.at("FFDC File").push_back(msg.str());
152             }
153             else
154             {
155                 debugData.emplace("FFDC File",
156                                   std::vector<std::string>{msg.str()});
157             }
158 
159             continue;
160         }
161 
162         addUserDataSection(std::move(ud));
163     }
164 
165 #ifdef PEL_ENABLE_PHAL
166     auto path = std::string(OBJ_ENTRY) + '/' + std::to_string(obmcLogID);
167     openpower::pels::phal::createServiceActions(callouts, path, dataIface,
168                                                 plid());
169 #endif
170 
171     // Store in the PEL any important debug data created while
172     // building the PEL sections.
173     if (!debugData.empty())
174     {
175         nlohmann::json data;
176         data["PEL Internal Debug Data"] = debugData;
177         ud = util::makeJSONUserDataSection(data);
178 
179         addUserDataSection(std::move(ud));
180 
181         // Also put in the journal for debug
182         for (const auto& [name, data] : debugData)
183         {
184             for (const auto& message : data)
185             {
186                 std::string entry = name + ": " + message;
187                 log<level::INFO>(entry.c_str());
188             }
189         }
190     }
191 
192     _ph->setSectionCount(2 + _optionalSections.size());
193 
194     checkRulesAndFix();
195 }
196 
197 PEL::PEL(std::vector<uint8_t>& data) : PEL(data, 0)
198 {
199 }
200 
201 PEL::PEL(std::vector<uint8_t>& data, uint32_t obmcLogID)
202 {
203     populateFromRawData(data, obmcLogID);
204 }
205 
206 void PEL::populateFromRawData(std::vector<uint8_t>& data, uint32_t obmcLogID)
207 {
208     Stream pelData{data};
209     _ph = std::make_unique<PrivateHeader>(pelData);
210     if (obmcLogID != 0)
211     {
212         _ph->setOBMCLogID(obmcLogID);
213     }
214 
215     _uh = std::make_unique<UserHeader>(pelData);
216 
217     // Use the section factory to create the rest of the objects
218     for (size_t i = 2; i < _ph->sectionCount(); i++)
219     {
220         auto section = section_factory::create(pelData);
221         _optionalSections.push_back(std::move(section));
222     }
223 }
224 
225 bool PEL::valid() const
226 {
227     bool valid = _ph->valid();
228 
229     if (valid)
230     {
231         valid = _uh->valid();
232     }
233 
234     if (valid)
235     {
236         if (!std::all_of(_optionalSections.begin(), _optionalSections.end(),
237                          [](const auto& section) { return section->valid(); }))
238         {
239             valid = false;
240         }
241     }
242 
243     return valid;
244 }
245 
246 void PEL::setCommitTime()
247 {
248     auto now = std::chrono::system_clock::now();
249     _ph->setCommitTimestamp(getBCDTime(now));
250 }
251 
252 void PEL::assignID()
253 {
254     _ph->setID(generatePELID());
255 }
256 
257 void PEL::flatten(std::vector<uint8_t>& pelBuffer) const
258 {
259     Stream pelData{pelBuffer};
260 
261     if (!valid())
262     {
263         log<level::WARNING>("Unflattening an invalid PEL");
264     }
265 
266     _ph->flatten(pelData);
267     _uh->flatten(pelData);
268 
269     for (auto& section : _optionalSections)
270     {
271         section->flatten(pelData);
272     }
273 }
274 
275 std::vector<uint8_t> PEL::data() const
276 {
277     std::vector<uint8_t> pelData;
278     flatten(pelData);
279     return pelData;
280 }
281 
282 size_t PEL::size() const
283 {
284     size_t size = 0;
285 
286     if (_ph)
287     {
288         size += _ph->header().size;
289     }
290 
291     if (_uh)
292     {
293         size += _uh->header().size;
294     }
295 
296     for (const auto& section : _optionalSections)
297     {
298         size += section->header().size;
299     }
300 
301     return size;
302 }
303 
304 std::optional<SRC*> PEL::primarySRC() const
305 {
306     auto src = std::find_if(
307         _optionalSections.begin(), _optionalSections.end(), [](auto& section) {
308             return section->header().id ==
309                    static_cast<uint16_t>(SectionID::primarySRC);
310         });
311     if (src != _optionalSections.end())
312     {
313         return static_cast<SRC*>(src->get());
314     }
315 
316     return std::nullopt;
317 }
318 
319 void PEL::checkRulesAndFix()
320 {
321     // Only fix if the action flags are at their default value which
322     // means they weren't specified in the registry.  Otherwise
323     // assume the user knows what they are doing.
324     if (_uh->actionFlags() == actionFlagsDefault)
325     {
326         auto [actionFlags, eventType] =
327             pel_rules::check(0, _uh->eventType(), _uh->severity());
328 
329         _uh->setActionFlags(actionFlags);
330         _uh->setEventType(eventType);
331     }
332 }
333 
334 void PEL::printSectionInJSON(const Section& section, std::string& buf,
335                              std::map<uint16_t, size_t>& pluralSections,
336                              message::Registry& registry,
337                              const std::vector<std::string>& plugins,
338                              uint8_t creatorID) const
339 {
340     char tmpB[5];
341     uint8_t id[] = {static_cast<uint8_t>(section.header().id >> 8),
342                     static_cast<uint8_t>(section.header().id)};
343     sprintf(tmpB, "%c%c", id[0], id[1]);
344     std::string sectionID(tmpB);
345     std::string sectionName = pv::sectionTitles.count(sectionID)
346                                   ? pv::sectionTitles.at(sectionID)
347                                   : "Unknown Section";
348 
349     // Add a count if there are multiple of this type of section
350     auto count = pluralSections.find(section.header().id);
351     if (count != pluralSections.end())
352     {
353         sectionName += " " + std::to_string(count->second);
354         count->second++;
355     }
356 
357     if (section.valid())
358     {
359         std::optional<std::string> json;
360         if (sectionID == "PS" || sectionID == "SS")
361         {
362             json = section.getJSON(registry, plugins, creatorID);
363         }
364         else if ((sectionID == "UD") || (sectionID == "ED"))
365         {
366             json = section.getJSON(creatorID, plugins);
367         }
368         else
369         {
370             json = section.getJSON();
371         }
372 
373         buf += "\"" + sectionName + "\": {\n";
374 
375         if (json)
376         {
377             buf += *json + "\n},\n";
378         }
379         else
380         {
381             jsonInsert(buf, pv::sectionVer,
382                        getNumberString("%d", section.header().version), 1);
383             jsonInsert(buf, pv::subSection,
384                        getNumberString("%d", section.header().subType), 1);
385             jsonInsert(buf, pv::createdBy,
386                        getNumberString("0x%X", section.header().componentID),
387                        1);
388 
389             std::vector<uint8_t> data;
390             Stream s{data};
391             section.flatten(s);
392             std::string dstr =
393                 dumpHex(std::data(data) + SectionHeader::flattenedSize(),
394                         data.size() - SectionHeader::flattenedSize(), 2);
395             std::string jsonIndent(indentLevel, 0x20);
396             buf += jsonIndent + "\"Data\": [\n";
397             buf += dstr;
398             buf += jsonIndent + "]\n";
399             buf += "},\n";
400         }
401     }
402     else
403     {
404         buf += "\n\"Invalid Section\": [\n    \"invalid\"\n],\n";
405     }
406 }
407 
408 std::map<uint16_t, size_t> PEL::getPluralSections() const
409 {
410     std::map<uint16_t, size_t> sectionCounts;
411 
412     for (const auto& section : optionalSections())
413     {
414         if (sectionCounts.find(section->header().id) == sectionCounts.end())
415         {
416             sectionCounts[section->header().id] = 1;
417         }
418         else
419         {
420             sectionCounts[section->header().id]++;
421         }
422     }
423 
424     std::map<uint16_t, size_t> sections;
425     for (const auto& [id, count] : sectionCounts)
426     {
427         if (count > 1)
428         {
429             // Start with 0 here and printSectionInJSON()
430             // will increment it as it goes.
431             sections.emplace(id, 0);
432         }
433     }
434 
435     return sections;
436 }
437 
438 void PEL::toJSON(message::Registry& registry,
439                  const std::vector<std::string>& plugins) const
440 {
441     auto sections = getPluralSections();
442 
443     std::string buf = "{\n";
444     printSectionInJSON(*(_ph.get()), buf, sections, registry, plugins);
445     printSectionInJSON(*(_uh.get()), buf, sections, registry, plugins);
446     for (auto& section : this->optionalSections())
447     {
448         printSectionInJSON(*(section.get()), buf, sections, registry, plugins,
449                            _ph->creatorID());
450     }
451     buf += "}";
452     std::size_t found = buf.rfind(",");
453     if (found != std::string::npos)
454         buf.replace(found, 1, "");
455     std::cout << buf << std::endl;
456 }
457 
458 bool PEL::addUserDataSection(std::unique_ptr<UserData> userData)
459 {
460     if (size() + userData->header().size > _maxPELSize)
461     {
462         if (userData->shrink(_maxPELSize - size()))
463         {
464             _optionalSections.push_back(std::move(userData));
465         }
466         else
467         {
468             log<level::WARNING>(
469                 "Could not shrink UserData section. Dropping",
470                 entry("SECTION_SIZE=%d\n", userData->header().size),
471                 entry("COMPONENT_ID=0x%02X", userData->header().componentID),
472                 entry("SUBTYPE=0x%X", userData->header().subType),
473                 entry("VERSION=0x%X", userData->header().version));
474             return false;
475         }
476     }
477     else
478     {
479         _optionalSections.push_back(std::move(userData));
480     }
481     return true;
482 }
483 
484 nlohmann::json PEL::getCalloutJSON(const PelFFDC& ffdcFiles)
485 {
486     nlohmann::json callouts;
487 
488     for (const auto& file : ffdcFiles)
489     {
490         if ((file.format == UserDataFormat::json) &&
491             (file.subType == jsonCalloutSubtype))
492         {
493             auto data = util::readFD(file.fd);
494             if (data.empty())
495             {
496                 throw std::runtime_error{
497                     "Could not get data from JSON callout file descriptor"};
498             }
499 
500             std::string jsonString{data.begin(), data.begin() + data.size()};
501 
502             callouts = nlohmann::json::parse(jsonString);
503             break;
504         }
505     }
506 
507     return callouts;
508 }
509 
510 bool PEL::isHwCalloutPresent() const
511 {
512     auto pSRC = primarySRC();
513     if (!pSRC)
514     {
515         return false;
516     }
517 
518     bool calloutPresent = false;
519     if ((*pSRC)->callouts())
520     {
521         for (auto& i : (*pSRC)->callouts()->callouts())
522         {
523             if (((*i).fruIdentity()))
524             {
525                 auto& fruId = (*i).fruIdentity();
526                 if ((*fruId).failingComponentType() ==
527                     src::FRUIdentity::hardwareFRU)
528                 {
529                     calloutPresent = true;
530                     break;
531                 }
532             }
533         }
534     }
535 
536     return calloutPresent;
537 }
538 
539 void PEL::updateSysInfoInExtendedUserDataSection(
540     const DataInterfaceBase& dataIface)
541 {
542     const AdditionalData additionalData;
543 
544     // Check for PEL from Hostboot
545     if (_ph->creatorID() == static_cast<uint8_t>(CreatorID::hostboot))
546     {
547         // Get the ED section from PEL
548         auto op = std::find_if(_optionalSections.begin(),
549                                _optionalSections.end(), [](auto& section) {
550                                    return section->header().id ==
551                                           static_cast<uint16_t>(
552                                               SectionID::extUserData);
553                                });
554 
555         // Check for ED section found and its not the last section of PEL
556         if (op != _optionalSections.end())
557         {
558             // Get the extended user data class mapped to found section
559             auto extUserData = static_cast<ExtendedUserData*>(op->get());
560 
561             // Check for the creator ID is for OpenBMC
562             if (extUserData->creatorID() ==
563                 static_cast<uint8_t>(CreatorID::openBMC))
564             {
565                 // Update subtype and component id
566                 auto subType = static_cast<uint8_t>(UserDataFormat::json);
567                 auto componentId =
568                     static_cast<uint16_t>(ComponentID::phosphorLogging);
569 
570                 // Update system data to ED section
571                 auto ud =
572                     util::makeSysInfoUserDataSection(additionalData, dataIface);
573                 extUserData->updateDataSection(subType, componentId,
574                                                ud->data());
575             }
576         }
577     }
578 }
579 
580 void PEL::updateTerminateBitInSRCSection()
581 {
582     //  Check for pel severity of type - 0x51 = critical error, system
583     //  termination
584     if (_uh->severity() == 0x51)
585     {
586         // Get the primary SRC section
587         auto pSRC = primarySRC();
588         if (pSRC)
589         {
590             (*pSRC)->setTerminateBit();
591         }
592     }
593 }
594 
595 namespace util
596 {
597 
598 std::unique_ptr<UserData> makeJSONUserDataSection(const nlohmann::json& json)
599 {
600     auto jsonString = json.dump();
601     std::vector<uint8_t> jsonData(jsonString.begin(), jsonString.end());
602 
603     // Pad to a 4 byte boundary
604     while ((jsonData.size() % 4) != 0)
605     {
606         jsonData.push_back(0);
607     }
608 
609     return std::make_unique<UserData>(
610         static_cast<uint16_t>(ComponentID::phosphorLogging),
611         static_cast<uint8_t>(UserDataFormat::json),
612         static_cast<uint8_t>(UserDataFormatVersion::json), jsonData);
613 }
614 
615 std::unique_ptr<UserData> makeADUserDataSection(const AdditionalData& ad)
616 {
617     assert(!ad.empty());
618     nlohmann::json json;
619 
620     // Remove the 'ESEL' entry, as it contains a full PEL in the value.
621     if (ad.getValue("ESEL"))
622     {
623         auto newAD = ad;
624         newAD.remove("ESEL");
625         json = newAD.toJSON();
626     }
627     else
628     {
629         json = ad.toJSON();
630     }
631 
632     return makeJSONUserDataSection(json);
633 }
634 
635 void addProcessNameToJSON(nlohmann::json& json,
636                           const std::optional<std::string>& pid,
637                           const DataInterfaceBase& dataIface)
638 {
639     std::string name{unknownValue};
640 
641     try
642     {
643         if (pid)
644         {
645             auto n = dataIface.getProcessName(*pid);
646             if (n)
647             {
648                 name = *n;
649             }
650         }
651     }
652     catch (const std::exception& e)
653     {
654     }
655 
656     if (pid)
657     {
658         json["Process Name"] = std::move(name);
659     }
660 }
661 
662 void addBMCFWVersionIDToJSON(nlohmann::json& json,
663                              const DataInterfaceBase& dataIface)
664 {
665     auto id = dataIface.getBMCFWVersionID();
666     if (id.empty())
667     {
668         id = unknownValue;
669     }
670 
671     json["FW Version ID"] = std::move(id);
672 }
673 
674 std::string lastSegment(char separator, std::string data)
675 {
676     auto pos = data.find_last_of(separator);
677     if (pos != std::string::npos)
678     {
679         data = data.substr(pos + 1);
680     }
681 
682     return data;
683 }
684 
685 void addIMKeyword(nlohmann::json& json, const DataInterfaceBase& dataIface)
686 {
687     auto keyword = dataIface.getSystemIMKeyword();
688 
689     std::string value{};
690 
691     std::for_each(keyword.begin(), keyword.end(), [&](const auto& byte) {
692         value += fmt::format("{:02X}", byte);
693     });
694 
695     json["System IM"] = value;
696 }
697 
698 void addStatesToJSON(nlohmann::json& json, const DataInterfaceBase& dataIface)
699 {
700     json["BMCState"] = lastSegment('.', dataIface.getBMCState());
701     json["ChassisState"] = lastSegment('.', dataIface.getChassisState());
702     json["HostState"] = lastSegment('.', dataIface.getHostState());
703     json["BootState"] = lastSegment('.', dataIface.getBootState());
704 }
705 
706 std::unique_ptr<UserData>
707     makeSysInfoUserDataSection(const AdditionalData& ad,
708                                const DataInterfaceBase& dataIface)
709 {
710     nlohmann::json json;
711 
712     addProcessNameToJSON(json, ad.getValue("_PID"), dataIface);
713     addBMCFWVersionIDToJSON(json, dataIface);
714     addIMKeyword(json, dataIface);
715     addStatesToJSON(json, dataIface);
716 
717     return makeJSONUserDataSection(json);
718 }
719 
720 std::vector<uint8_t> readFD(int fd)
721 {
722     std::vector<uint8_t> data;
723 
724     // Get the size
725     struct stat s;
726     int r = fstat(fd, &s);
727     if (r != 0)
728     {
729         auto e = errno;
730         log<level::ERR>("Could not get FFDC file size from FD",
731                         entry("ERRNO=%d", e));
732         return data;
733     }
734 
735     if (0 == s.st_size)
736     {
737         log<level::ERR>("FFDC file is empty");
738         return data;
739     }
740 
741     data.resize(s.st_size);
742 
743     // Make sure its at the beginning, as maybe another
744     // extension already used it.
745     r = lseek(fd, 0, SEEK_SET);
746     if (r == -1)
747     {
748         auto e = errno;
749         log<level::ERR>("Could not seek to beginning of FFDC file",
750                         entry("ERRNO=%d", e));
751         return data;
752     }
753 
754     r = read(fd, data.data(), s.st_size);
755     if (r == -1)
756     {
757         auto e = errno;
758         log<level::ERR>("Could not read FFDC file", entry("ERRNO=%d", e));
759     }
760     else if (r != s.st_size)
761     {
762         log<level::WARNING>("Could not read full FFDC file",
763                             entry("FILE_SIZE=%d", s.st_size),
764                             entry("SIZE_READ=%d", r));
765     }
766 
767     return data;
768 }
769 
770 std::unique_ptr<UserData> makeFFDCuserDataSection(uint16_t componentID,
771                                                   const PelFFDCfile& file)
772 {
773     auto data = readFD(file.fd);
774 
775     if (data.empty())
776     {
777         return std::unique_ptr<UserData>();
778     }
779 
780     // The data needs 4 Byte alignment, and save amount padded for the
781     // CBOR case.
782     uint32_t pad = 0;
783     while (data.size() % 4)
784     {
785         data.push_back(0);
786         pad++;
787     }
788 
789     // For JSON, CBOR, and Text use our component ID, subType, and version,
790     // otherwise use the supplied ones.
791     uint16_t compID = static_cast<uint16_t>(ComponentID::phosphorLogging);
792     uint8_t subType{};
793     uint8_t version{};
794 
795     switch (file.format)
796     {
797         case UserDataFormat::json:
798             subType = static_cast<uint8_t>(UserDataFormat::json);
799             version = static_cast<uint8_t>(UserDataFormatVersion::json);
800             break;
801         case UserDataFormat::cbor:
802             subType = static_cast<uint8_t>(UserDataFormat::cbor);
803             version = static_cast<uint8_t>(UserDataFormatVersion::cbor);
804 
805             // The CBOR parser will fail on the extra pad bytes since they
806             // aren't CBOR.  Add the amount we padded to the end and other
807             // code will remove it all before parsing.
808             {
809                 data.resize(data.size() + 4);
810                 Stream stream{data};
811                 stream.offset(data.size() - 4);
812                 stream << pad;
813             }
814 
815             break;
816         case UserDataFormat::text:
817             subType = static_cast<uint8_t>(UserDataFormat::text);
818             version = static_cast<uint8_t>(UserDataFormatVersion::text);
819             break;
820         case UserDataFormat::custom:
821         default:
822             // Use the passed in values
823             compID = componentID;
824             subType = file.subType;
825             version = file.version;
826             break;
827     }
828 
829     return std::make_unique<UserData>(compID, subType, version, data);
830 }
831 
832 } // namespace util
833 
834 } // namespace pels
835 } // namespace openpower
836