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