xref: /openbmc/openpower-vpd-parser/ibm_vpd_utils.cpp (revision 6555e7efc40c305e5c3c8863c3d1793f474ec7e2)
1 #include "config.h"
2 
3 #include "ibm_vpd_utils.hpp"
4 
5 #include "common_utility.hpp"
6 #include "defines.hpp"
7 #include "vpd_exceptions.hpp"
8 
9 #include <boost/algorithm/string.hpp>
10 #include <filesystem>
11 #include <fstream>
12 #include <gpiod.hpp>
13 #include <iomanip>
14 #include <nlohmann/json.hpp>
15 #include <phosphor-logging/elog-errors.hpp>
16 #include <phosphor-logging/log.hpp>
17 #include <regex>
18 #include <sdbusplus/server.hpp>
19 #include <sstream>
20 #include <vector>
21 #include <xyz/openbmc_project/Common/error.hpp>
22 
23 using json = nlohmann::json;
24 
25 namespace openpower
26 {
27 namespace vpd
28 {
29 using namespace openpower::vpd::constants;
30 using namespace inventory;
31 using namespace phosphor::logging;
32 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
33 using namespace record;
34 using namespace openpower::vpd::exceptions;
35 using namespace common::utility;
36 using Severity = openpower::vpd::constants::PelSeverity;
37 namespace fs = std::filesystem;
38 
39 // mapping of severity enum to severity interface
40 static std::unordered_map<Severity, std::string> sevMap = {
41     {Severity::INFORMATIONAL,
42      "xyz.openbmc_project.Logging.Entry.Level.Informational"},
43     {Severity::DEBUG, "xyz.openbmc_project.Logging.Entry.Level.Debug"},
44     {Severity::NOTICE, "xyz.openbmc_project.Logging.Entry.Level.Notice"},
45     {Severity::WARNING, "xyz.openbmc_project.Logging.Entry.Level.Warning"},
46     {Severity::CRITICAL, "xyz.openbmc_project.Logging.Entry.Level.Critical"},
47     {Severity::EMERGENCY, "xyz.openbmc_project.Logging.Entry.Level.Emergency"},
48     {Severity::ERROR, "xyz.openbmc_project.Logging.Entry.Level.Error"},
49     {Severity::ALERT, "xyz.openbmc_project.Logging.Entry.Level.Alert"}};
50 
51 namespace inventory
52 {
53 
54 MapperResponse
55     getObjectSubtreeForInterfaces(const std::string& root, const int32_t depth,
56                                   const std::vector<std::string>& interfaces)
57 {
58     auto bus = sdbusplus::bus::new_default();
59     auto mapperCall = bus.new_method_call(mapperDestination, mapperObjectPath,
60                                           mapperInterface, "GetSubTree");
61     mapperCall.append(root);
62     mapperCall.append(depth);
63     mapperCall.append(interfaces);
64 
65     MapperResponse result = {};
66 
67     try
68     {
69         auto response = bus.call(mapperCall);
70 
71         response.read(result);
72     }
73     catch (const sdbusplus::exception_t& e)
74     {
75         log<level::ERR>("Error in mapper GetSubTree",
76                         entry("ERROR=%s", e.what()));
77     }
78 
79     return result;
80 }
81 
82 } // namespace inventory
83 
84 LE2ByteData readUInt16LE(Binary::const_iterator iterator)
85 {
86     LE2ByteData lowByte = *iterator;
87     LE2ByteData highByte = *(iterator + 1);
88     lowByte |= (highByte << 8);
89     return lowByte;
90 }
91 
92 /** @brief Encodes a keyword for D-Bus.
93  */
94 std::string encodeKeyword(const std::string& kw, const std::string& encoding)
95 {
96     if (encoding == "MAC")
97     {
98         std::string res{};
99         size_t first = kw[0];
100         res += toHex(first >> 4);
101         res += toHex(first & 0x0f);
102         for (size_t i = 1; i < kw.size(); ++i)
103         {
104             res += ":";
105             res += toHex(kw[i] >> 4);
106             res += toHex(kw[i] & 0x0f);
107         }
108         return res;
109     }
110     else if (encoding == "DATE")
111     {
112         // Date, represent as
113         // <year>-<month>-<day> <hour>:<min>
114         std::string res{};
115         static constexpr uint8_t skipPrefix = 3;
116 
117         auto strItr = kw.begin();
118         advance(strItr, skipPrefix);
119         for_each(strItr, kw.end(), [&res](size_t c) { res += c; });
120 
121         res.insert(BD_YEAR_END, 1, '-');
122         res.insert(BD_MONTH_END, 1, '-');
123         res.insert(BD_DAY_END, 1, ' ');
124         res.insert(BD_HOUR_END, 1, ':');
125 
126         return res;
127     }
128     else // default to string encoding
129     {
130         return std::string(kw.begin(), kw.end());
131     }
132 }
133 
134 std::string readBusProperty(const std::string& obj, const std::string& inf,
135                             const std::string& prop)
136 {
137     std::string propVal{};
138     std::string object = INVENTORY_PATH + obj;
139     auto bus = sdbusplus::bus::new_default();
140     auto properties = bus.new_method_call(
141         "xyz.openbmc_project.Inventory.Manager", object.c_str(),
142         "org.freedesktop.DBus.Properties", "Get");
143     properties.append(inf);
144     properties.append(prop);
145     auto result = bus.call(properties);
146     if (!result.is_method_error())
147     {
148         inventory::Value val;
149         result.read(val);
150         if (auto pVal = std::get_if<Binary>(&val))
151         {
152             propVal.assign(reinterpret_cast<const char*>(pVal->data()),
153                            pVal->size());
154         }
155         else if (auto pVal = std::get_if<std::string>(&val))
156         {
157             propVal.assign(pVal->data(), pVal->size());
158         }
159         else if (auto pVal = get_if<bool>(&val))
160         {
161             if (*pVal == false)
162             {
163                 propVal = "false";
164             }
165             else
166             {
167                 propVal = "true";
168             }
169         }
170     }
171     return propVal;
172 }
173 
174 void createPEL(const std::map<std::string, std::string>& additionalData,
175                const Severity& sev, const std::string& errIntf, sd_bus* sdBus)
176 {
177     // This pointer will be NULL in case the call is made from ibm-read-vpd. In
178     // that case a sync call will do.
179     if (sdBus == nullptr)
180     {
181         createSyncPEL(additionalData, sev, errIntf);
182     }
183     else
184     {
185         std::string errDescription{};
186         auto pos = additionalData.find("DESCRIPTION");
187         if (pos != additionalData.end())
188         {
189             errDescription = pos->second;
190         }
191         else
192         {
193             errDescription = "Description field missing in additional data";
194         }
195 
196         std::string pelSeverity =
197             "xyz.openbmc_project.Logging.Entry.Level.Error";
198         auto itr = sevMap.find(sev);
199         if (itr != sevMap.end())
200         {
201             pelSeverity = itr->second;
202         }
203 
204         // Implies this is a call from Manager. Hence we need to make an async
205         // call to avoid deadlock with Phosphor-logging.
206         auto rc = sd_bus_call_method_async(
207             sdBus, NULL, loggerService, loggerObjectPath, loggerCreateInterface,
208             "Create", NULL, NULL, "ssa{ss}", errIntf.c_str(),
209             pelSeverity.c_str(), 1, "DESCRIPTION", errDescription.c_str());
210 
211         if (rc < 0)
212         {
213             log<level::ERR>("Error calling sd_bus_call_method_async",
214                             entry("RC=%d", rc), entry("MSG=%s", strerror(-rc)));
215         }
216     }
217 }
218 
219 void createSyncPEL(const std::map<std::string, std::string>& additionalData,
220                    const Severity& sev, const std::string& errIntf)
221 {
222     try
223     {
224         std::string pelSeverity =
225             "xyz.openbmc_project.Logging.Entry.Level.Error";
226         auto bus = sdbusplus::bus::new_default();
227         auto service = getService(bus, loggerObjectPath, loggerCreateInterface);
228         auto method = bus.new_method_call(service.c_str(), loggerObjectPath,
229                                           loggerCreateInterface, "Create");
230 
231         auto itr = sevMap.find(sev);
232         if (itr != sevMap.end())
233         {
234             pelSeverity = itr->second;
235         }
236 
237         method.append(errIntf, pelSeverity, additionalData);
238         auto resp = bus.call(method);
239     }
240     catch (const sdbusplus::exception_t& e)
241     {
242         std::cerr << "Dbus call to phosphor-logging Create failed. Reason:"
243                   << e.what();
244     }
245 }
246 
247 inventory::VPDfilepath getVpdFilePath(const std::string& jsonFile,
248                                       const std::string& ObjPath)
249 {
250     std::ifstream inventoryJson(jsonFile);
251     const auto& jsonObject = json::parse(inventoryJson);
252     inventory::VPDfilepath filePath{};
253 
254     if (jsonObject.find("frus") == jsonObject.end())
255     {
256         throw(VpdJsonException(
257             "Invalid JSON structure - frus{} object not found in ", jsonFile));
258     }
259 
260     const nlohmann::json& groupFRUS =
261         jsonObject["frus"].get_ref<const nlohmann::json::object_t&>();
262     for (const auto& itemFRUS : groupFRUS.items())
263     {
264         const std::vector<nlohmann::json>& groupEEPROM =
265             itemFRUS.value().get_ref<const nlohmann::json::array_t&>();
266         for (const auto& itemEEPROM : groupEEPROM)
267         {
268             if (itemEEPROM["inventoryPath"]
269                     .get_ref<const nlohmann::json::string_t&>() == ObjPath)
270             {
271                 filePath = itemFRUS.key();
272                 return filePath;
273             }
274         }
275     }
276 
277     return filePath;
278 }
279 
280 bool isPathInJson(const std::string& eepromPath)
281 {
282     bool present = false;
283     std::ifstream inventoryJson(INVENTORY_JSON_SYM_LINK);
284 
285     try
286     {
287         auto js = json::parse(inventoryJson);
288         if (js.find("frus") == js.end())
289         {
290             throw(VpdJsonException(
291                 "Invalid JSON structure - frus{} object not found in ",
292                 INVENTORY_JSON_SYM_LINK));
293         }
294         json fruJson = js["frus"];
295 
296         if (fruJson.find(eepromPath) != fruJson.end())
297         {
298             present = true;
299         }
300     }
301     catch (const json::parse_error& ex)
302     {
303         throw(VpdJsonException("Json Parsing failed", INVENTORY_JSON_SYM_LINK));
304     }
305     return present;
306 }
307 
308 bool isRecKwInDbusJson(const std::string& recordName,
309                        const std::string& keyword)
310 {
311     std::ifstream propertyJson(DBUS_PROP_JSON);
312     json dbusProperty;
313     bool present = false;
314 
315     if (propertyJson.is_open())
316     {
317         try
318         {
319             auto dbusPropertyJson = json::parse(propertyJson);
320             if (dbusPropertyJson.find("dbusProperties") ==
321                 dbusPropertyJson.end())
322             {
323                 throw(VpdJsonException("dbusProperties{} object not found in "
324                                        "DbusProperties json : ",
325                                        DBUS_PROP_JSON));
326             }
327 
328             dbusProperty = dbusPropertyJson["dbusProperties"];
329             if (dbusProperty.contains(recordName))
330             {
331                 const std::vector<std::string>& kwdsToPublish =
332                     dbusProperty[recordName];
333                 if (find(kwdsToPublish.begin(), kwdsToPublish.end(), keyword) !=
334                     kwdsToPublish.end()) // present
335                 {
336                     present = true;
337                 }
338             }
339         }
340         catch (const json::parse_error& ex)
341         {
342             throw(VpdJsonException("Json Parsing failed", DBUS_PROP_JSON));
343         }
344     }
345     else
346     {
347         // If dbus properties json is not available, we assume the given
348         // record-keyword is part of dbus-properties json. So setting the bool
349         // variable to true.
350         present = true;
351     }
352     return present;
353 }
354 
355 vpdType vpdTypeCheck(const Binary& vpdVector)
356 {
357     // Read first 3 Bytes to check the 11S bar code format
358     std::string is11SFormat = "";
359     for (uint8_t i = 0; i < FORMAT_11S_LEN; i++)
360     {
361         is11SFormat += vpdVector[MEMORY_VPD_DATA_START + i];
362     }
363 
364     if (vpdVector[IPZ_DATA_START] == KW_VAL_PAIR_START_TAG)
365     {
366         // IPZ VPD FORMAT
367         return vpdType::IPZ_VPD;
368     }
369     else if (vpdVector[KW_VPD_DATA_START] == KW_VPD_START_TAG)
370     {
371         // KEYWORD VPD FORMAT
372         return vpdType::KEYWORD_VPD;
373     }
374     else if (((vpdVector[SPD_BYTE_3] & SPD_BYTE_BIT_0_3_MASK) ==
375               SPD_MODULE_TYPE_DDIMM) &&
376              (is11SFormat.compare(MEMORY_VPD_START_TAG)))
377     {
378         // DDIMM Memory VPD format
379         if ((vpdVector[SPD_BYTE_2] & SPD_BYTE_MASK) == SPD_DRAM_TYPE_DDR5)
380         {
381             return vpdType::DDR5_DDIMM_MEMORY_VPD;
382         }
383         else if ((vpdVector[SPD_BYTE_2] & SPD_BYTE_MASK) == SPD_DRAM_TYPE_DDR4)
384         {
385             return vpdType::DDR4_DDIMM_MEMORY_VPD;
386         }
387     }
388     else if ((vpdVector[SPD_BYTE_2] & SPD_BYTE_MASK) == SPD_DRAM_TYPE_DDR5)
389     {
390         // ISDIMM Memory VPD format
391         return vpdType::DDR5_ISDIMM_MEMORY_VPD;
392     }
393     else if ((vpdVector[SPD_BYTE_2] & SPD_BYTE_MASK) == SPD_DRAM_TYPE_DDR4)
394     {
395         // ISDIMM Memory VPD format
396         return vpdType::DDR4_ISDIMM_MEMORY_VPD;
397     }
398 
399     // INVALID VPD FORMAT
400     return vpdType::INVALID_VPD_FORMAT;
401 }
402 
403 const std::string getIM(const Parsed& vpdMap)
404 {
405     Binary imVal;
406     auto property = vpdMap.find("VSBP");
407     if (property != vpdMap.end())
408     {
409         auto kw = (property->second).find("IM");
410         if (kw != (property->second).end())
411         {
412             copy(kw->second.begin(), kw->second.end(), back_inserter(imVal));
413         }
414     }
415 
416     std::ostringstream oss;
417     for (auto& i : imVal)
418     {
419         oss << std::setw(2) << std::setfill('0') << std::hex
420             << static_cast<int>(i);
421     }
422 
423     return oss.str();
424 }
425 
426 const std::string getHW(const Parsed& vpdMap)
427 {
428     Binary hwVal;
429     auto prop = vpdMap.find("VINI");
430     if (prop != vpdMap.end())
431     {
432         auto kw = (prop->second).find("HW");
433         if (kw != (prop->second).end())
434         {
435             copy(kw->second.begin(), kw->second.end(), back_inserter(hwVal));
436         }
437     }
438 
439     // The planar pass only comes from the LSB of the HW keyword,
440     // where as the MSB is used for other purposes such as signifying clock
441     // termination.
442     hwVal[0] = 0x00;
443 
444     std::ostringstream hwString;
445     for (auto& i : hwVal)
446     {
447         hwString << std::setw(2) << std::setfill('0') << std::hex
448                  << static_cast<int>(i);
449     }
450 
451     return hwString.str();
452 }
453 
454 std::string getSystemsJson(const Parsed& vpdMap)
455 {
456     std::string jsonPath = "/usr/share/vpd/";
457     std::string jsonName{};
458 
459     std::ifstream systemJson(SYSTEM_JSON);
460     if (!systemJson)
461     {
462         throw((VpdJsonException("Failed to access Json path", SYSTEM_JSON)));
463     }
464 
465     try
466     {
467         auto js = json::parse(systemJson);
468 
469         std::string hwKeyword = getHW(vpdMap);
470         const std::string imKeyword = getIM(vpdMap);
471 
472         transform(hwKeyword.begin(), hwKeyword.end(), hwKeyword.begin(),
473                   ::toupper);
474 
475         if (js.find("system") == js.end())
476         {
477             throw std::runtime_error("Invalid systems Json");
478         }
479 
480         if (js["system"].find(imKeyword) == js["system"].end())
481         {
482             throw std::runtime_error(
483                 "Invalid system. This system type is not present "
484                 "in the systemsJson. IM: " +
485                 imKeyword);
486         }
487 
488         if ((js["system"][imKeyword].find("constraint") !=
489              js["system"][imKeyword].end()) &&
490             js["system"][imKeyword]["constraint"].find("HW") !=
491                 js["system"][imKeyword]["constraint"].end())
492         {
493             // collect hw versions from json, and check hwKeyword  is part of it
494             // if hwKeyword is found there then load respective json
495             // otherwise load default one.
496             for (const auto& hwVersion :
497                  js["system"][imKeyword]["constraint"]["HW"])
498             {
499                 std::string hw = hwVersion;
500                 transform(hw.begin(), hw.end(), hw.begin(), ::toupper);
501 
502                 if (hw == hwKeyword)
503                 {
504                     jsonName = js["system"][imKeyword]["constraint"]["json"];
505                     break;
506                 }
507             }
508 
509             if (jsonName.empty() && js["system"][imKeyword].find("default") !=
510                                         js["system"][imKeyword].end())
511             {
512                 jsonName = js["system"][imKeyword]["default"];
513             }
514         }
515         else if (js["system"][imKeyword].find("default") !=
516                  js["system"][imKeyword].end())
517         {
518             jsonName = js["system"][imKeyword]["default"];
519         }
520         else
521         {
522             throw std::runtime_error(
523                 "Bad System json. Neither constraint nor default found");
524         }
525 
526         jsonPath += jsonName;
527     }
528 
529     catch (const json::parse_error& ex)
530     {
531         throw(VpdJsonException("Json Parsing failed", SYSTEM_JSON));
532     }
533     return jsonPath;
534 }
535 
536 void udevToGenericPath(std::string& file)
537 {
538     // Sample udevEvent i2c path :
539     // "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a480.i2c-bus/i2c-8/8-0051/8-00510/nvmem"
540     // find if the path contains the word i2c in it.
541     if (file.find("i2c") != std::string::npos)
542     {
543         std::string i2cBusAddr{};
544 
545         // Every udev i2c path should have the common pattern
546         // "i2c-bus_number/bus_number-vpd_address". Search for
547         // "bus_number-vpd_address".
548         std::regex i2cPattern("((i2c)-[0-9]+\\/)([0-9]+-[0-9]{4})");
549         std::smatch match;
550         if (std::regex_search(file, match, i2cPattern))
551         {
552             i2cBusAddr = match.str(3);
553         }
554         else
555         {
556             std::cerr << "The given udev path < " << file
557                       << " > doesn't match the required pattern. Skipping VPD "
558                          "collection."
559                       << std::endl;
560             exit(EXIT_SUCCESS);
561         }
562         // Forming the generic file path
563         file = i2cPathPrefix + i2cBusAddr + "/eeprom";
564     }
565     // Sample udevEvent spi path :
566     // "/sys/devices/platform/ahb/ahb:apb/1e79b000.fsi/fsi-master/fsi0/slave@00:00/00:00:00:04/spi_master/spi2/spi2.0/spi2.00/nvmem"
567     // find if the path contains the word spi in it.
568     else if (file.find("spi") != std::string::npos)
569     {
570         // Every udev spi path will have common pattern "spi<Digit>/", which
571         // describes the spi bus number at which the fru is connected; Followed
572         // by a slash following the vpd address of the fru. Taking the above
573         // input as a common key, we try to search for the pattern "spi<Digit>/"
574         // using regular expression.
575         std::regex spiPattern("((spi)[0-9]+)(\\/)");
576         std::string spiBus{};
577         std::smatch match;
578         if (std::regex_search(file, match, spiPattern))
579         {
580             spiBus = match.str(1);
581         }
582         else
583         {
584             std::cerr << "The given udev path < " << file
585                       << " > doesn't match the required pattern. Skipping VPD "
586                          "collection."
587                       << std::endl;
588             exit(EXIT_SUCCESS);
589         }
590         // Forming the generic path
591         file = spiPathPrefix + spiBus + ".0/eeprom";
592     }
593     else
594     {
595         std::cerr << "\n The given EEPROM path < " << file
596                   << " > is not valid. It's neither I2C nor "
597                      "SPI path. Skipping VPD collection.."
598                   << std::endl;
599         exit(EXIT_SUCCESS);
600     }
601 }
602 std::string getBadVpdName(const std::string& file)
603 {
604     std::string badVpd = BAD_VPD_DIR;
605     if (file.find("i2c") != std::string::npos)
606     {
607         badVpd += "i2c-";
608         std::regex i2cPattern("(at24/)([0-9]+-[0-9]+)\\/");
609         std::smatch match;
610         if (std::regex_search(file, match, i2cPattern))
611         {
612             badVpd += match.str(2);
613         }
614     }
615     else if (file.find("spi") != std::string::npos)
616     {
617         std::regex spiPattern("((spi)[0-9]+)(.0)");
618         std::smatch match;
619         if (std::regex_search(file, match, spiPattern))
620         {
621             badVpd += match.str(1);
622         }
623     }
624     return badVpd;
625 }
626 
627 void dumpBadVpd(const std::string& file, const Binary& vpdVector)
628 {
629     fs::path badVpdDir = BAD_VPD_DIR;
630     fs::create_directory(badVpdDir);
631     std::string badVpdPath = getBadVpdName(file);
632     if (fs::exists(badVpdPath))
633     {
634         std::error_code ec;
635         fs::remove(badVpdPath, ec);
636         if (ec) // error code
637         {
638             std::string error = "Error removing the existing broken vpd in ";
639             error += badVpdPath;
640             error += ". Error code : ";
641             error += ec.value();
642             error += ". Error message : ";
643             error += ec.message();
644             throw std::runtime_error(error);
645         }
646     }
647     std::ofstream badVpdFileStream(badVpdPath, std::ofstream::binary);
648     if (!badVpdFileStream)
649     {
650         throw std::runtime_error(
651             "Failed to open bad vpd file path in /tmp/bad-vpd. "
652             "Unable to dump the broken/bad vpd file.");
653     }
654     badVpdFileStream.write(reinterpret_cast<const char*>(vpdVector.data()),
655                            vpdVector.size());
656 }
657 
658 const std::string getKwVal(const Parsed& vpdMap, const std::string& rec,
659                            const std::string& kwd)
660 {
661     std::string kwVal{};
662 
663     auto findRec = vpdMap.find(rec);
664 
665     // check if record is found in map we got by parser
666     if (findRec != vpdMap.end())
667     {
668         auto findKwd = findRec->second.find(kwd);
669 
670         if (findKwd != findRec->second.end())
671         {
672             kwVal = findKwd->second;
673         }
674     }
675 
676     return kwVal;
677 }
678 
679 std::string byteArrayToHexString(const Binary& vec)
680 {
681     std::stringstream ss;
682     std::string hexRep = "0x";
683     ss << hexRep;
684     std::string str = ss.str();
685 
686     // convert Decimal to Hex string
687     for (auto& v : vec)
688     {
689         ss << std::setfill('0') << std::setw(2) << std::hex << (int)v;
690         str = ss.str();
691     }
692     return str;
693 }
694 
695 std::string getPrintableValue(const Binary& vec)
696 {
697     std::string str{};
698 
699     // find for a non printable value in the vector
700     const auto it = std::find_if(vec.begin(), vec.end(),
701                                  [](const auto& ele) { return !isprint(ele); });
702 
703     if (it != vec.end()) // if the given vector has any non printable value
704     {
705         for (auto itr = it; itr != vec.end(); itr++)
706         {
707             if (*itr != 0x00)
708             {
709                 str = byteArrayToHexString(vec);
710                 return str;
711             }
712         }
713         str = std::string(vec.begin(), it);
714     }
715     else
716     {
717         str = std::string(vec.begin(), vec.end());
718     }
719     return str;
720 }
721 
722 void executePostFailAction(const nlohmann::json& json, const std::string& file)
723 {
724     if ((json["frus"][file].at(0)).find("postActionFail") ==
725         json["frus"][file].at(0).end())
726     {
727         return;
728     }
729 
730     uint8_t pinValue = 0;
731     std::string pinName;
732 
733     for (const auto& postAction :
734          (json["frus"][file].at(0))["postActionFail"].items())
735     {
736         if (postAction.key() == "pin")
737         {
738             pinName = postAction.value();
739         }
740         else if (postAction.key() == "value")
741         {
742             // Get the value to set
743             pinValue = postAction.value();
744         }
745     }
746 
747     std::cout << "Setting GPIO: " << pinName << " to " << (int)pinValue
748               << std::endl;
749 
750     try
751     {
752         gpiod::line outputLine = gpiod::find_line(pinName);
753 
754         if (!outputLine)
755         {
756             throw GpioException(
757                 "Couldn't find output line for the GPIO. Skipping "
758                 "this GPIO action.");
759         }
760         outputLine.request(
761             {"Disable line", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
762             pinValue);
763     }
764     catch (const std::exception& e)
765     {
766         std::string i2cBusAddr;
767         std::string errMsg = e.what();
768         errMsg += "\nGPIO: " + pinName;
769 
770         if ((json["frus"][file].at(0)["postActionFail"].find(
771                 "gpioI2CAddress")) !=
772             json["frus"][file].at(0)["postActionFail"].end())
773         {
774             i2cBusAddr =
775                 json["frus"][file].at(0)["postActionFail"]["gpioI2CAddress"];
776             errMsg += " i2cBusAddress: " + i2cBusAddr;
777         }
778 
779         throw GpioException(e.what());
780     }
781 
782     return;
783 }
784 
785 std::optional<bool> isPresent(const nlohmann::json& json,
786                               const std::string& file)
787 
788 {
789     if ((json["frus"][file].at(0)).find("presence") !=
790         json["frus"][file].at(0).end())
791     {
792         if (((json["frus"][file].at(0)["presence"]).find("pin") !=
793              json["frus"][file].at(0)["presence"].end()) &&
794             ((json["frus"][file].at(0)["presence"]).find("value") !=
795              json["frus"][file].at(0)["presence"].end()))
796         {
797             std::string presPinName =
798                 json["frus"][file].at(0)["presence"]["pin"];
799             Byte presPinValue = json["frus"][file].at(0)["presence"]["value"];
800 
801             try
802             {
803                 gpiod::line presenceLine = gpiod::find_line(presPinName);
804 
805                 if (!presenceLine)
806                 {
807                     std::cerr << "Couldn't find the presence line for - "
808                               << presPinName << std::endl;
809 
810                     throw GpioException(
811                         "Couldn't find the presence line for the "
812                         "GPIO. Skipping this GPIO action.");
813                 }
814 
815                 presenceLine.request({"Read the presence line",
816                                       gpiod::line_request::DIRECTION_INPUT, 0});
817 
818                 Byte gpioData = presenceLine.get_value();
819 
820                 return (gpioData == presPinValue);
821             }
822             catch (const std::exception& e)
823             {
824                 std::string i2cBusAddr;
825                 std::string errMsg = e.what();
826                 errMsg += " GPIO : " + presPinName;
827 
828                 if ((json["frus"][file].at(0)["presence"])
829                         .find("gpioI2CAddress") !=
830                     json["frus"][file].at(0)["presence"].end())
831                 {
832                     i2cBusAddr =
833                         json["frus"][file].at(0)["presence"]["gpioI2CAddress"];
834                     errMsg += " i2cBusAddress: " + i2cBusAddr;
835                 }
836 
837                 // Take failure postAction
838                 executePostFailAction(json, file);
839                 throw GpioException(errMsg);
840             }
841         }
842         else
843         {
844             // missing required informations
845             std::cerr
846                 << "VPD inventory JSON missing basic informations of presence "
847                    "for this FRU : ["
848                 << file << "]. Executing executePostFailAction." << std::endl;
849 
850             // Take failure postAction
851             executePostFailAction(json, file);
852 
853             return false;
854         }
855     }
856     return std::optional<bool>{};
857 }
858 
859 bool executePreAction(const nlohmann::json& json, const std::string& file)
860 {
861     auto present = isPresent(json, file);
862     if (present && !present.value())
863     {
864         executePostFailAction(json, file);
865         return false;
866     }
867 
868     if ((json["frus"][file].at(0)).find("preAction") !=
869         json["frus"][file].at(0).end())
870     {
871         if (((json["frus"][file].at(0)["preAction"]).find("pin") !=
872              json["frus"][file].at(0)["preAction"].end()) &&
873             ((json["frus"][file].at(0)["preAction"]).find("value") !=
874              json["frus"][file].at(0)["preAction"].end()))
875         {
876             std::string pinName = json["frus"][file].at(0)["preAction"]["pin"];
877             // Get the value to set
878             Byte pinValue = json["frus"][file].at(0)["preAction"]["value"];
879 
880             std::cout << "Setting GPIO: " << pinName << " to " << (int)pinValue
881                       << std::endl;
882             try
883             {
884                 gpiod::line outputLine = gpiod::find_line(pinName);
885 
886                 if (!outputLine)
887                 {
888                     std::cerr << "Couldn't find the line for output pin - "
889                               << pinName << std::endl;
890                     throw GpioException(
891                         "Couldn't find output line for the GPIO. "
892                         "Skipping this GPIO action.");
893                 }
894                 outputLine.request({"FRU pre-action",
895                                     ::gpiod::line_request::DIRECTION_OUTPUT, 0},
896                                    pinValue);
897             }
898             catch (const std::exception& e)
899             {
900                 std::string i2cBusAddr;
901                 std::string errMsg = e.what();
902                 errMsg += " GPIO : " + pinName;
903 
904                 if ((json["frus"][file].at(0)["preAction"])
905                         .find("gpioI2CAddress") !=
906                     json["frus"][file].at(0)["preAction"].end())
907                 {
908                     i2cBusAddr =
909                         json["frus"][file].at(0)["preAction"]["gpioI2CAddress"];
910                     errMsg += " i2cBusAddress: " + i2cBusAddr;
911                 }
912 
913                 // Take failure postAction
914                 executePostFailAction(json, file);
915                 throw GpioException(errMsg);
916             }
917         }
918         else
919         {
920             // missing required informations
921             std::cerr
922                 << "VPD inventory JSON missing basic informations of preAction "
923                    "for this FRU : ["
924                 << file << "]. Executing executePostFailAction." << std::endl;
925 
926             // Take failure postAction
927             executePostFailAction(json, file);
928             return false;
929         }
930     }
931     return true;
932 }
933 
934 void insertOrMerge(inventory::InterfaceMap& map,
935                    const inventory::Interface& interface,
936                    inventory::PropertyMap&& property)
937 {
938     if (map.find(interface) != map.end())
939     {
940         auto& prop = map.at(interface);
941         prop.insert(property.begin(), property.end());
942     }
943     else
944     {
945         map.emplace(interface, property);
946     }
947 }
948 
949 BIOSAttrValueType readBIOSAttribute(const std::string& attrName)
950 {
951     std::tuple<std::string, BIOSAttrValueType, BIOSAttrValueType> attrVal;
952     auto bus = sdbusplus::bus::new_default();
953     auto method = bus.new_method_call(
954         "xyz.openbmc_project.BIOSConfigManager",
955         "/xyz/openbmc_project/bios_config/manager",
956         "xyz.openbmc_project.BIOSConfig.Manager", "GetAttribute");
957     method.append(attrName);
958     try
959     {
960         auto result = bus.call(method);
961         result.read(std::get<0>(attrVal), std::get<1>(attrVal),
962                     std::get<2>(attrVal));
963     }
964     catch (const sdbusplus::exception::SdBusError& e)
965     {
966         std::cerr << "Failed to read BIOS Attribute: " << attrName << std::endl;
967         std::cerr << e.what() << std::endl;
968     }
969     return std::get<1>(attrVal);
970 }
971 
972 std::string getPowerState()
973 {
974     // TODO: How do we handle multiple chassis?
975     std::string powerState{};
976     auto bus = sdbusplus::bus::new_default();
977     auto properties =
978         bus.new_method_call("xyz.openbmc_project.State.Chassis",
979                             "/xyz/openbmc_project/state/chassis0",
980                             "org.freedesktop.DBus.Properties", "Get");
981     properties.append("xyz.openbmc_project.State.Chassis");
982     properties.append("CurrentPowerState");
983     auto result = bus.call(properties);
984     if (!result.is_method_error())
985     {
986         std::variant<std::string> val;
987         result.read(val);
988         if (auto pVal = std::get_if<std::string>(&val))
989         {
990             powerState = *pVal;
991         }
992     }
993     std::cout << "Power state is: " << powerState << std::endl;
994     return powerState;
995 }
996 
997 Binary getVpdDataInVector(const nlohmann::json& js, const std::string& file)
998 {
999     uint32_t offset = 0;
1000     // check if offset present?
1001     for (const auto& item : js["frus"][file])
1002     {
1003         if (item.find("offset") != item.end())
1004         {
1005             offset = item["offset"];
1006         }
1007     }
1008 
1009     // TODO: Figure out a better way to get max possible VPD size.
1010     auto maxVPDSize = std::min(std::filesystem::file_size(file),
1011                                static_cast<uintmax_t>(65504));
1012 
1013     Binary vpdVector;
1014     vpdVector.resize(maxVPDSize);
1015     std::ifstream vpdFile;
1016     vpdFile.open(file, std::ios::binary);
1017 
1018     vpdFile.seekg(offset, std::ios_base::cur);
1019     vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), maxVPDSize);
1020     vpdVector.resize(vpdFile.gcount());
1021 
1022     // Make sure we reset the EEPROM pointer to a "safe" location if it was DIMM
1023     // SPD that we just read.
1024     for (const auto& item : js["frus"][file])
1025     {
1026         if (item.find("extraInterfaces") != item.end())
1027         {
1028             if (item["extraInterfaces"].find(
1029                     "xyz.openbmc_project.Inventory.Item.Dimm") !=
1030                 item["extraInterfaces"].end())
1031             {
1032                 // moves the EEPROM pointer to 2048 'th byte.
1033                 vpdFile.seekg(2047, std::ios::beg);
1034                 // Read that byte and discard - to affirm the move
1035                 // operation.
1036                 char ch;
1037                 vpdFile.read(&ch, sizeof(ch));
1038                 break;
1039             }
1040         }
1041     }
1042 
1043     return vpdVector;
1044 }
1045 } // namespace vpd
1046 } // namespace openpower
1047