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, const std::string& driver)
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 + driver + "/" + 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 + driver + "/" + 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 hexString(const std::variant<Binary, std::string>& kw)
680 {
681     std::string hexString;
682     std::visit(
683         [&hexString](auto&& kw) {
684             for (auto& kwVal : kw)
685             {
686                 std::stringstream ss;
687                 std::string hexRep = "0x";
688                 ss << hexRep;
689                 ss << std::setfill('0') << std::setw(2) << std::hex
690                    << static_cast<int>(kwVal);
691                 hexString = ss.str();
692             }
693         },
694         kw);
695     return hexString;
696 }
697 
698 std::string getPrintableValue(const std::variant<Binary, std::string>& kwVal)
699 {
700     std::string kwString{};
701     std::visit(
702         [&kwString](auto&& kwVal) {
703             const auto it =
704                 std::find_if(kwVal.begin(), kwVal.end(),
705                              [](const auto& kw) { return !isprint(kw); });
706             if (it != kwVal.end())
707             {
708                 bool printable = true;
709                 for (auto itr = it; itr != kwVal.end(); itr++)
710                 {
711                     if (*itr != 0x00)
712                     {
713                         kwString = hexString(kwVal);
714                         printable = false;
715                         break;
716                     }
717                 }
718                 if (printable)
719                 {
720                     kwString = std::string(kwVal.begin(), it);
721                 }
722             }
723             else
724             {
725                 kwString = std::string(kwVal.begin(), kwVal.end());
726             }
727         },
728         kwVal);
729     return kwString;
730 }
731 
732 void executePostFailAction(const nlohmann::json& json, const std::string& file)
733 {
734     if ((json["frus"][file].at(0)).find("postActionFail") ==
735         json["frus"][file].at(0).end())
736     {
737         return;
738     }
739 
740     uint8_t pinValue = 0;
741     std::string pinName;
742 
743     for (const auto& postAction :
744          (json["frus"][file].at(0))["postActionFail"].items())
745     {
746         if (postAction.key() == "pin")
747         {
748             pinName = postAction.value();
749         }
750         else if (postAction.key() == "value")
751         {
752             // Get the value to set
753             pinValue = postAction.value();
754         }
755     }
756 
757     std::cout << "Setting GPIO: " << pinName << " to " << (int)pinValue
758               << std::endl;
759 
760     try
761     {
762         gpiod::line outputLine = gpiod::find_line(pinName);
763 
764         if (!outputLine)
765         {
766             throw GpioException(
767                 "Couldn't find output line for the GPIO. Skipping "
768                 "this GPIO action.");
769         }
770         outputLine.request(
771             {"Disable line", ::gpiod::line_request::DIRECTION_OUTPUT, 0},
772             pinValue);
773     }
774     catch (const std::exception& e)
775     {
776         std::string i2cBusAddr;
777         std::string errMsg = e.what();
778         errMsg += "\nGPIO: " + pinName;
779 
780         if ((json["frus"][file].at(0)["postActionFail"].find(
781                 "gpioI2CAddress")) !=
782             json["frus"][file].at(0)["postActionFail"].end())
783         {
784             i2cBusAddr =
785                 json["frus"][file].at(0)["postActionFail"]["gpioI2CAddress"];
786             errMsg += " i2cBusAddress: " + i2cBusAddr;
787         }
788 
789         throw GpioException(e.what());
790     }
791 
792     return;
793 }
794 
795 std::optional<bool> isPresent(const nlohmann::json& json,
796                               const std::string& file)
797 
798 {
799     if ((json["frus"][file].at(0)).find("presence") !=
800         json["frus"][file].at(0).end())
801     {
802         if (((json["frus"][file].at(0)["presence"]).find("pin") !=
803              json["frus"][file].at(0)["presence"].end()) &&
804             ((json["frus"][file].at(0)["presence"]).find("value") !=
805              json["frus"][file].at(0)["presence"].end()))
806         {
807             std::string presPinName =
808                 json["frus"][file].at(0)["presence"]["pin"];
809             Byte presPinValue = json["frus"][file].at(0)["presence"]["value"];
810 
811             try
812             {
813                 gpiod::line presenceLine = gpiod::find_line(presPinName);
814 
815                 if (!presenceLine)
816                 {
817                     std::cerr << "Couldn't find the presence line for - "
818                               << presPinName << std::endl;
819 
820                     throw GpioException(
821                         "Couldn't find the presence line for the "
822                         "GPIO. Skipping this GPIO action.");
823                 }
824 
825                 presenceLine.request({"Read the presence line",
826                                       gpiod::line_request::DIRECTION_INPUT, 0});
827 
828                 Byte gpioData = presenceLine.get_value();
829 
830                 return (gpioData == presPinValue);
831             }
832             catch (const std::exception& e)
833             {
834                 std::string i2cBusAddr;
835                 std::string errMsg = e.what();
836                 errMsg += " GPIO : " + presPinName;
837 
838                 if ((json["frus"][file].at(0)["presence"])
839                         .find("gpioI2CAddress") !=
840                     json["frus"][file].at(0)["presence"].end())
841                 {
842                     i2cBusAddr =
843                         json["frus"][file].at(0)["presence"]["gpioI2CAddress"];
844                     errMsg += " i2cBusAddress: " + i2cBusAddr;
845                 }
846 
847                 // Take failure postAction
848                 executePostFailAction(json, file);
849                 throw GpioException(errMsg);
850             }
851         }
852         else
853         {
854             // missing required informations
855             std::cerr
856                 << "VPD inventory JSON missing basic informations of presence "
857                    "for this FRU : ["
858                 << file << "]. Executing executePostFailAction." << std::endl;
859 
860             // Take failure postAction
861             executePostFailAction(json, file);
862 
863             return false;
864         }
865     }
866     return std::optional<bool>{};
867 }
868 
869 bool executePreAction(const nlohmann::json& json, const std::string& file)
870 {
871     auto present = isPresent(json, file);
872     if (present && !present.value())
873     {
874         executePostFailAction(json, file);
875         return false;
876     }
877 
878     if ((json["frus"][file].at(0)).find("preAction") !=
879         json["frus"][file].at(0).end())
880     {
881         if (((json["frus"][file].at(0)["preAction"]).find("pin") !=
882              json["frus"][file].at(0)["preAction"].end()) &&
883             ((json["frus"][file].at(0)["preAction"]).find("value") !=
884              json["frus"][file].at(0)["preAction"].end()))
885         {
886             std::string pinName = json["frus"][file].at(0)["preAction"]["pin"];
887             // Get the value to set
888             Byte pinValue = json["frus"][file].at(0)["preAction"]["value"];
889 
890             std::cout << "Setting GPIO: " << pinName << " to " << (int)pinValue
891                       << std::endl;
892             try
893             {
894                 gpiod::line outputLine = gpiod::find_line(pinName);
895 
896                 if (!outputLine)
897                 {
898                     std::cerr << "Couldn't find the line for output pin - "
899                               << pinName << std::endl;
900                     throw GpioException(
901                         "Couldn't find output line for the GPIO. "
902                         "Skipping this GPIO action.");
903                 }
904                 outputLine.request({"FRU pre-action",
905                                     ::gpiod::line_request::DIRECTION_OUTPUT, 0},
906                                    pinValue);
907             }
908             catch (const std::exception& e)
909             {
910                 std::string i2cBusAddr;
911                 std::string errMsg = e.what();
912                 errMsg += " GPIO : " + pinName;
913 
914                 if ((json["frus"][file].at(0)["preAction"])
915                         .find("gpioI2CAddress") !=
916                     json["frus"][file].at(0)["preAction"].end())
917                 {
918                     i2cBusAddr =
919                         json["frus"][file].at(0)["preAction"]["gpioI2CAddress"];
920                     errMsg += " i2cBusAddress: " + i2cBusAddr;
921                 }
922 
923                 // Take failure postAction
924                 executePostFailAction(json, file);
925                 throw GpioException(errMsg);
926             }
927         }
928         else
929         {
930             // missing required informations
931             std::cerr
932                 << "VPD inventory JSON missing basic informations of preAction "
933                    "for this FRU : ["
934                 << file << "]. Executing executePostFailAction." << std::endl;
935 
936             // Take failure postAction
937             executePostFailAction(json, file);
938             return false;
939         }
940     }
941     return true;
942 }
943 
944 void insertOrMerge(inventory::InterfaceMap& map,
945                    const inventory::Interface& interface,
946                    inventory::PropertyMap&& property)
947 {
948     if (map.find(interface) != map.end())
949     {
950         auto& prop = map.at(interface);
951         prop.insert(property.begin(), property.end());
952     }
953     else
954     {
955         map.emplace(interface, property);
956     }
957 }
958 
959 BIOSAttrValueType readBIOSAttribute(const std::string& attrName)
960 {
961     std::tuple<std::string, BIOSAttrValueType, BIOSAttrValueType> attrVal;
962     auto bus = sdbusplus::bus::new_default();
963     auto method = bus.new_method_call(
964         "xyz.openbmc_project.BIOSConfigManager",
965         "/xyz/openbmc_project/bios_config/manager",
966         "xyz.openbmc_project.BIOSConfig.Manager", "GetAttribute");
967     method.append(attrName);
968     try
969     {
970         auto result = bus.call(method);
971         result.read(std::get<0>(attrVal), std::get<1>(attrVal),
972                     std::get<2>(attrVal));
973     }
974     catch (const sdbusplus::exception::SdBusError& e)
975     {
976         std::cerr << "Failed to read BIOS Attribute: " << attrName << std::endl;
977         std::cerr << e.what() << std::endl;
978     }
979     return std::get<1>(attrVal);
980 }
981 
982 std::string getPowerState()
983 {
984     // TODO: How do we handle multiple chassis?
985     std::string powerState{};
986     auto bus = sdbusplus::bus::new_default();
987     auto properties =
988         bus.new_method_call("xyz.openbmc_project.State.Chassis",
989                             "/xyz/openbmc_project/state/chassis0",
990                             "org.freedesktop.DBus.Properties", "Get");
991     properties.append("xyz.openbmc_project.State.Chassis");
992     properties.append("CurrentPowerState");
993     auto result = bus.call(properties);
994     if (!result.is_method_error())
995     {
996         std::variant<std::string> val;
997         result.read(val);
998         if (auto pVal = std::get_if<std::string>(&val))
999         {
1000             powerState = *pVal;
1001         }
1002     }
1003     std::cout << "Power state is: " << powerState << std::endl;
1004     return powerState;
1005 }
1006 
1007 Binary getVpdDataInVector(const nlohmann::json& js, const std::string& file)
1008 {
1009     uint32_t offset = 0;
1010     // check if offset present?
1011     for (const auto& item : js["frus"][file])
1012     {
1013         if (item.find("offset") != item.end())
1014         {
1015             offset = item["offset"];
1016         }
1017     }
1018 
1019     // TODO: Figure out a better way to get max possible VPD size.
1020     auto maxVPDSize = std::min(std::filesystem::file_size(file),
1021                                static_cast<uintmax_t>(65504));
1022 
1023     Binary vpdVector;
1024     vpdVector.resize(maxVPDSize);
1025     std::ifstream vpdFile;
1026     vpdFile.open(file, std::ios::binary);
1027 
1028     vpdFile.seekg(offset, std::ios_base::cur);
1029     vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), maxVPDSize);
1030     vpdVector.resize(vpdFile.gcount());
1031 
1032     // Make sure we reset the EEPROM pointer to a "safe" location if it was DIMM
1033     // SPD that we just read.
1034     for (const auto& item : js["frus"][file])
1035     {
1036         if (item.find("extraInterfaces") != item.end())
1037         {
1038             if (item["extraInterfaces"].find(
1039                     "xyz.openbmc_project.Inventory.Item.Dimm") !=
1040                 item["extraInterfaces"].end())
1041             {
1042                 // moves the EEPROM pointer to 2048 'th byte.
1043                 vpdFile.seekg(2047, std::ios::beg);
1044                 // Read that byte and discard - to affirm the move
1045                 // operation.
1046                 char ch;
1047                 vpdFile.read(&ch, sizeof(ch));
1048                 break;
1049             }
1050         }
1051     }
1052 
1053     return vpdVector;
1054 }
1055 } // namespace vpd
1056 } // namespace openpower