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