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