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