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