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