blob: 42bb99a3e610463f826c5f6676d15c5d74fd6139 [file] [log] [blame]
SunnySrivastava198443306542020-04-01 02:50:20 -05001#include "config.h"
2
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -05003#include "ibm_vpd_utils.hpp"
Patrick Venturec83c4dc2018-11-01 16:29:18 -07004
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -05005#include "common_utility.hpp"
SunnySrivastava1984d076da82020-03-05 05:33:35 -06006#include "defines.hpp"
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +05307#include "vpd_exceptions.hpp"
SunnySrivastava1984d076da82020-03-05 05:33:35 -06008
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -06009#include <filesystem>
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +053010#include <fstream>
11#include <iomanip>
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -050012#include <nlohmann/json.hpp>
SunnySrivastava19849094d4f2020-08-05 09:32:29 -050013#include <phosphor-logging/elog-errors.hpp>
Patrick Venturec83c4dc2018-11-01 16:29:18 -070014#include <phosphor-logging/log.hpp>
PriyangaRamasamy647868e2020-09-08 17:03:19 +053015#include <regex>
Patrick Venturec83c4dc2018-11-01 16:29:18 -070016#include <sdbusplus/server.hpp>
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +053017#include <sstream>
18#include <vector>
SunnySrivastava19849094d4f2020-08-05 09:32:29 -050019#include <xyz/openbmc_project/Common/error.hpp>
Deepak Kodihalli76794492017-02-16 23:48:18 -060020
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +053021using json = nlohmann::json;
22
Deepak Kodihalli76794492017-02-16 23:48:18 -060023namespace openpower
24{
25namespace vpd
26{
SunnySrivastava1984945a02d2020-05-06 01:55:41 -050027using namespace openpower::vpd::constants;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -050028using namespace inventory;
29using namespace phosphor::logging;
SunnySrivastava19849094d4f2020-08-05 09:32:29 -050030using namespace sdbusplus::xyz::openbmc_project::Common::Error;
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +053031using namespace record;
32using namespace openpower::vpd::exceptions;
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -050033using namespace common::utility;
Sunny Srivastava0746eee2021-03-22 13:36:54 -050034using Severity = openpower::vpd::constants::PelSeverity;
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -060035namespace fs = std::filesystem;
Sunny Srivastava0746eee2021-03-22 13:36:54 -050036
37// mapping of severity enum to severity interface
38static std::unordered_map<Severity, std::string> sevMap = {
39 {Severity::INFORMATIONAL,
40 "xyz.openbmc_project.Logging.Entry.Level.Informational"},
41 {Severity::DEBUG, "xyz.openbmc_project.Logging.Entry.Level.Debug"},
42 {Severity::NOTICE, "xyz.openbmc_project.Logging.Entry.Level.Notice"},
43 {Severity::WARNING, "xyz.openbmc_project.Logging.Entry.Level.Warning"},
44 {Severity::CRITICAL, "xyz.openbmc_project.Logging.Entry.Level.Critical"},
45 {Severity::EMERGENCY, "xyz.openbmc_project.Logging.Entry.Level.Emergency"},
46 {Severity::ERROR, "xyz.openbmc_project.Logging.Entry.Level.Error"},
47 {Severity::ALERT, "xyz.openbmc_project.Logging.Entry.Level.Alert"}};
48
Deepak Kodihalli76794492017-02-16 23:48:18 -060049namespace inventory
50{
51
SunnySrivastava19849094d4f2020-08-05 09:32:29 -050052MapperResponse
53 getObjectSubtreeForInterfaces(const std::string& root, const int32_t depth,
54 const std::vector<std::string>& interfaces)
55{
56 auto bus = sdbusplus::bus::new_default();
57 auto mapperCall = bus.new_method_call(mapperDestination, mapperObjectPath,
58 mapperInterface, "GetSubTree");
59 mapperCall.append(root);
60 mapperCall.append(depth);
61 mapperCall.append(interfaces);
62
63 MapperResponse result = {};
64
65 try
66 {
67 auto response = bus.call(mapperCall);
68
69 response.read(result);
70 }
Patrick Williams8be43342021-09-02 09:33:36 -050071 catch (const sdbusplus::exception::exception& e)
SunnySrivastava19849094d4f2020-08-05 09:32:29 -050072 {
73 log<level::ERR>("Error in mapper GetSubTree",
74 entry("ERROR=%s", e.what()));
75 }
76
77 return result;
78}
79
Deepak Kodihalli76794492017-02-16 23:48:18 -060080} // namespace inventory
81
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -060082LE2ByteData readUInt16LE(Binary::const_iterator iterator)
83{
84 LE2ByteData lowByte = *iterator;
85 LE2ByteData highByte = *(iterator + 1);
86 lowByte |= (highByte << 8);
87 return lowByte;
88}
89
SunnySrivastava1984d076da82020-03-05 05:33:35 -060090/** @brief Encodes a keyword for D-Bus.
91 */
92string encodeKeyword(const string& kw, const string& encoding)
93{
94 if (encoding == "MAC")
95 {
96 string res{};
97 size_t first = kw[0];
98 res += toHex(first >> 4);
99 res += toHex(first & 0x0f);
100 for (size_t i = 1; i < kw.size(); ++i)
101 {
102 res += ":";
103 res += toHex(kw[i] >> 4);
104 res += toHex(kw[i] & 0x0f);
105 }
106 return res;
107 }
108 else if (encoding == "DATE")
109 {
110 // Date, represent as
111 // <year>-<month>-<day> <hour>:<min>
112 string res{};
113 static constexpr uint8_t skipPrefix = 3;
114
115 auto strItr = kw.begin();
116 advance(strItr, skipPrefix);
117 for_each(strItr, kw.end(), [&res](size_t c) { res += c; });
118
119 res.insert(BD_YEAR_END, 1, '-');
120 res.insert(BD_MONTH_END, 1, '-');
121 res.insert(BD_DAY_END, 1, ' ');
122 res.insert(BD_HOUR_END, 1, ':');
123
124 return res;
125 }
126 else // default to string encoding
127 {
128 return string(kw.begin(), kw.end());
129 }
130}
SunnySrivastava198443306542020-04-01 02:50:20 -0500131
132string readBusProperty(const string& obj, const string& inf, const string& prop)
133{
134 std::string propVal{};
135 std::string object = INVENTORY_PATH + obj;
136 auto bus = sdbusplus::bus::new_default();
137 auto properties = bus.new_method_call(
138 "xyz.openbmc_project.Inventory.Manager", object.c_str(),
139 "org.freedesktop.DBus.Properties", "Get");
140 properties.append(inf);
141 properties.append(prop);
142 auto result = bus.call(properties);
143 if (!result.is_method_error())
144 {
SunnySrivastava1984bca5aaa2020-04-21 05:31:04 -0500145 variant<Binary, string> val;
SunnySrivastava198443306542020-04-01 02:50:20 -0500146 result.read(val);
SunnySrivastava198443306542020-04-01 02:50:20 -0500147 if (auto pVal = get_if<Binary>(&val))
148 {
149 propVal.assign(reinterpret_cast<const char*>(pVal->data()),
150 pVal->size());
151 }
SunnySrivastava1984bca5aaa2020-04-21 05:31:04 -0500152 else if (auto pVal = get_if<string>(&val))
153 {
154 propVal.assign(pVal->data(), pVal->size());
155 }
SunnySrivastava198443306542020-04-01 02:50:20 -0500156 }
157 return propVal;
158}
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500159
160void createPEL(const std::map<std::string, std::string>& additionalData,
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500161 const Severity& sev, const std::string& errIntf)
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500162{
163 try
164 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500165 std::string pelSeverity =
166 "xyz.openbmc_project.Logging.Entry.Level.Error";
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500167 auto bus = sdbusplus::bus::new_default();
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500168 auto service = getService(bus, loggerObjectPath, loggerCreateInterface);
169 auto method = bus.new_method_call(service.c_str(), loggerObjectPath,
170 loggerCreateInterface, "Create");
171
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500172 auto itr = sevMap.find(sev);
173 if (itr != sevMap.end())
174 {
175 pelSeverity = itr->second;
176 }
177
178 method.append(errIntf, pelSeverity, additionalData);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500179 auto resp = bus.call(method);
180 }
Patrick Williams8be43342021-09-02 09:33:36 -0500181 catch (const sdbusplus::exception::exception& e)
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500182 {
183 throw std::runtime_error(
184 "Error in invoking D-Bus logging create interface to register PEL");
185 }
186}
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +0530187
188inventory::VPDfilepath getVpdFilePath(const string& jsonFile,
189 const std::string& ObjPath)
190{
191 ifstream inventoryJson(jsonFile);
192 const auto& jsonObject = json::parse(inventoryJson);
193 inventory::VPDfilepath filePath{};
194
195 if (jsonObject.find("frus") == jsonObject.end())
196 {
197 throw(VpdJsonException(
198 "Invalid JSON structure - frus{} object not found in ", jsonFile));
199 }
200
201 const nlohmann::json& groupFRUS =
202 jsonObject["frus"].get_ref<const nlohmann::json::object_t&>();
203 for (const auto& itemFRUS : groupFRUS.items())
204 {
205 const std::vector<nlohmann::json>& groupEEPROM =
206 itemFRUS.value().get_ref<const nlohmann::json::array_t&>();
207 for (const auto& itemEEPROM : groupEEPROM)
208 {
209 if (itemEEPROM["inventoryPath"]
210 .get_ref<const nlohmann::json::string_t&>() == ObjPath)
211 {
212 filePath = itemFRUS.key();
213 return filePath;
214 }
215 }
216 }
217
218 return filePath;
219}
220
221bool isPathInJson(const std::string& eepromPath)
222{
223 bool present = false;
224 ifstream inventoryJson(INVENTORY_JSON_SYM_LINK);
225
226 try
227 {
228 auto js = json::parse(inventoryJson);
229 if (js.find("frus") == js.end())
230 {
231 throw(VpdJsonException(
232 "Invalid JSON structure - frus{} object not found in ",
233 INVENTORY_JSON_SYM_LINK));
234 }
235 json fruJson = js["frus"];
236
237 if (fruJson.find(eepromPath) != fruJson.end())
238 {
239 present = true;
240 }
241 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500242 catch (const json::parse_error& ex)
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +0530243 {
244 throw(VpdJsonException("Json Parsing failed", INVENTORY_JSON_SYM_LINK));
245 }
246 return present;
247}
248
249bool isRecKwInDbusJson(const std::string& recordName,
250 const std::string& keyword)
251{
252 ifstream propertyJson(DBUS_PROP_JSON);
253 json dbusProperty;
254 bool present = false;
255
256 if (propertyJson.is_open())
257 {
258 try
259 {
260 auto dbusPropertyJson = json::parse(propertyJson);
261 if (dbusPropertyJson.find("dbusProperties") ==
262 dbusPropertyJson.end())
263 {
264 throw(VpdJsonException("dbusProperties{} object not found in "
265 "DbusProperties json : ",
266 DBUS_PROP_JSON));
267 }
268
269 dbusProperty = dbusPropertyJson["dbusProperties"];
270 if (dbusProperty.contains(recordName))
271 {
272 const vector<string>& kwdsToPublish = dbusProperty[recordName];
273 if (find(kwdsToPublish.begin(), kwdsToPublish.end(), keyword) !=
274 kwdsToPublish.end()) // present
275 {
276 present = true;
277 }
278 }
279 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500280 catch (const json::parse_error& ex)
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +0530281 {
282 throw(VpdJsonException("Json Parsing failed", DBUS_PROP_JSON));
283 }
284 }
285 else
286 {
287 // If dbus properties json is not available, we assume the given
288 // record-keyword is part of dbus-properties json. So setting the bool
289 // variable to true.
290 present = true;
291 }
292 return present;
293}
294
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -0500295vpdType vpdTypeCheck(const Binary& vpdVector)
296{
297 // Read first 3 Bytes to check the 11S bar code format
298 std::string is11SFormat = "";
299 for (uint8_t i = 0; i < FORMAT_11S_LEN; i++)
300 {
301 is11SFormat += vpdVector[MEMORY_VPD_DATA_START + i];
302 }
303
304 if (vpdVector[IPZ_DATA_START] == KW_VAL_PAIR_START_TAG)
305 {
306 // IPZ VPD FORMAT
307 return vpdType::IPZ_VPD;
308 }
309 else if (vpdVector[KW_VPD_DATA_START] == KW_VPD_START_TAG)
310 {
311 // KEYWORD VPD FORMAT
312 return vpdType::KEYWORD_VPD;
313 }
314 else if (is11SFormat.compare(MEMORY_VPD_START_TAG) == 0)
315 {
316 // Memory VPD format
317 return vpdType::MEMORY_VPD;
318 }
319
320 // INVALID VPD FORMAT
321 return vpdType::INVALID_VPD_FORMAT;
322}
323
Alpana Kumarif05effd2021-04-07 07:32:53 -0500324const string getIM(const Parsed& vpdMap)
325{
326 Binary imVal;
327 auto property = vpdMap.find("VSBP");
328 if (property != vpdMap.end())
329 {
330 auto kw = (property->second).find("IM");
331 if (kw != (property->second).end())
332 {
333 copy(kw->second.begin(), kw->second.end(), back_inserter(imVal));
334 }
335 }
336
337 ostringstream oss;
338 for (auto& i : imVal)
339 {
340 oss << setw(2) << setfill('0') << hex << static_cast<int>(i);
341 }
342
343 return oss.str();
344}
345
346const string getHW(const Parsed& vpdMap)
347{
348 Binary hwVal;
349 auto prop = vpdMap.find("VINI");
350 if (prop != vpdMap.end())
351 {
352 auto kw = (prop->second).find("HW");
353 if (kw != (prop->second).end())
354 {
355 copy(kw->second.begin(), kw->second.end(), back_inserter(hwVal));
356 }
357 }
358
Alpana Kumari88d2ae82021-11-10 03:23:31 -0600359 // The planar pass only comes from the LSB of the HW keyword,
360 // where as the MSB is used for other purposes such as signifying clock
361 // termination.
362 hwVal[0] = 0x00;
363
Alpana Kumarif05effd2021-04-07 07:32:53 -0500364 ostringstream hwString;
365 for (auto& i : hwVal)
366 {
367 hwString << setw(2) << setfill('0') << hex << static_cast<int>(i);
368 }
369
370 return hwString.str();
371}
372
373string getSystemsJson(const Parsed& vpdMap)
374{
375 string jsonPath = "/usr/share/vpd/";
376 string jsonName{};
377
378 ifstream systemJson(SYSTEM_JSON);
379 if (!systemJson)
380 {
381 throw((VpdJsonException("Failed to access Json path", SYSTEM_JSON)));
382 }
383
384 try
385 {
386 auto js = json::parse(systemJson);
387
388 const string hwKeyword = getHW(vpdMap);
389 const string imKeyword = getIM(vpdMap);
390
391 if (js.find("system") == js.end())
392 {
393 throw runtime_error("Invalid systems Json");
394 }
395
396 if (js["system"].find(imKeyword) == js["system"].end())
397 {
398 throw runtime_error(
399 "Invalid system. This system type is not present "
400 "in the systemsJson. IM: " +
401 imKeyword);
402 }
403
404 if ((js["system"][imKeyword].find("constraint") !=
405 js["system"][imKeyword].end()) &&
406 (hwKeyword == js["system"][imKeyword]["constraint"]["HW"]))
407 {
408 jsonName = js["system"][imKeyword]["constraint"]["json"];
409 }
410 else if (js["system"][imKeyword].find("default") !=
411 js["system"][imKeyword].end())
412 {
413 jsonName = js["system"][imKeyword]["default"];
414 }
415 else
416 {
417 throw runtime_error(
418 "Bad System json. Neither constraint nor default found");
419 }
420
421 jsonPath += jsonName;
422 }
423
Patrick Williams8e15b932021-10-06 13:04:22 -0500424 catch (const json::parse_error& ex)
Alpana Kumarif05effd2021-04-07 07:32:53 -0500425 {
426 throw(VpdJsonException("Json Parsing failed", SYSTEM_JSON));
427 }
428 return jsonPath;
429}
430
PriyangaRamasamy647868e2020-09-08 17:03:19 +0530431void udevToGenericPath(string& file)
432{
433 // Sample udevEvent i2c path :
434 // "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a480.i2c-bus/i2c-8/8-0051/8-00510/nvmem"
435 // find if the path contains the word i2c in it.
436 if (file.find("i2c") != string::npos)
437 {
438 string i2cBusAddr{};
439
440 // Every udev i2c path should have the common pattern
441 // "i2c-bus_number/bus_number-vpd_address". Search for
442 // "bus_number-vpd_address".
443 regex i2cPattern("((i2c)-[0-9]+\\/)([0-9]+-[0-9]{4})");
444 smatch match;
445 if (regex_search(file, match, i2cPattern))
446 {
447 i2cBusAddr = match.str(3);
448 }
449 else
450 {
451 cerr << "The given udev path < " << file
452 << " > doesn't match the required pattern. Skipping VPD "
453 "collection."
454 << endl;
455 exit(EXIT_SUCCESS);
456 }
457 // Forming the generic file path
458 file = i2cPathPrefix + i2cBusAddr + "/eeprom";
459 }
460 // Sample udevEvent spi path :
461 // "/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"
462 // find if the path contains the word spi in it.
463 else if (file.find("spi") != string::npos)
464 {
465 // Every udev spi path will have common pattern "spi<Digit>/", which
466 // describes the spi bus number at which the fru is connected; Followed
467 // by a slash following the vpd address of the fru. Taking the above
468 // input as a common key, we try to search for the pattern "spi<Digit>/"
469 // using regular expression.
470 regex spiPattern("((spi)[0-9]+)(\\/)");
471 string spiBus{};
472 smatch match;
473 if (regex_search(file, match, spiPattern))
474 {
475 spiBus = match.str(1);
476 }
477 else
478 {
479 cerr << "The given udev path < " << file
480 << " > doesn't match the required pattern. Skipping VPD "
481 "collection."
482 << endl;
483 exit(EXIT_SUCCESS);
484 }
485 // Forming the generic path
486 file = spiPathPrefix + spiBus + ".0/eeprom";
487 }
488 else
489 {
490 cerr << "\n The given EEPROM path < " << file
491 << " > is not valid. It's neither I2C nor "
492 "SPI path. Skipping VPD collection.."
493 << endl;
494 exit(EXIT_SUCCESS);
495 }
496}
PriyangaRamasamyc2fe40f2021-03-02 06:27:33 -0600497string getBadVpdName(const string& file)
498{
499 string badVpd = BAD_VPD_DIR;
500 if (file.find("i2c") != string::npos)
501 {
502 badVpd += "i2c-";
503 regex i2cPattern("(at24/)([0-9]+-[0-9]+)\\/");
504 smatch match;
505 if (regex_search(file, match, i2cPattern))
506 {
507 badVpd += match.str(2);
508 }
509 }
510 else if (file.find("spi") != string::npos)
511 {
512 regex spiPattern("((spi)[0-9]+)(.0)");
513 smatch match;
514 if (regex_search(file, match, spiPattern))
515 {
516 badVpd += match.str(1);
517 }
518 }
519 return badVpd;
520}
521
522void dumpBadVpd(const string& file, const Binary& vpdVector)
523{
524 fs::path badVpdDir = BAD_VPD_DIR;
525 fs::create_directory(badVpdDir);
526 string badVpdPath = getBadVpdName(file);
527 if (fs::exists(badVpdPath))
528 {
529 std::error_code ec;
530 fs::remove(badVpdPath, ec);
531 if (ec) // error code
532 {
533 string error = "Error removing the existing broken vpd in ";
534 error += badVpdPath;
535 error += ". Error code : ";
536 error += ec.value();
537 error += ". Error message : ";
538 error += ec.message();
539 throw runtime_error(error);
540 }
541 }
542 ofstream badVpdFileStream(badVpdPath, ofstream::binary);
543 if (!badVpdFileStream)
544 {
545 throw runtime_error("Failed to open bad vpd file path in /tmp/bad-vpd. "
546 "Unable to dump the broken/bad vpd file.");
547 }
548 badVpdFileStream.write(reinterpret_cast<const char*>(vpdVector.data()),
549 vpdVector.size());
550}
alpana077ce68722021-07-25 13:23:59 -0500551
552const string getKwVal(const Parsed& vpdMap, const string& rec,
553 const string& kwd)
554{
555 string kwVal{};
556
557 auto findRec = vpdMap.find(rec);
558
559 // check if record is found in map we got by parser
560 if (findRec != vpdMap.end())
561 {
562 auto findKwd = findRec->second.find(kwd);
563
564 if (findKwd != findRec->second.end())
565 {
566 kwVal = findKwd->second;
567 }
568 }
569
570 return kwVal;
571}
572
Priyanga Ramasamyc9ecf8e2021-10-08 02:28:52 -0500573string byteArrayToHexString(const Binary& vec)
574{
575 stringstream ss;
576 string hexRep = "0x";
577 ss << hexRep;
578 string str = ss.str();
579
580 // convert Decimal to Hex string
581 for (auto& v : vec)
582 {
583 ss << setfill('0') << setw(2) << hex << (int)v;
584 str = ss.str();
585 }
586 return str;
587}
588
589string getPrintableValue(const Binary& vec)
Priyanga Ramasamy02434932021-10-07 16:26:05 -0500590{
591 string str{};
Priyanga Ramasamy02434932021-10-07 16:26:05 -0500592
Priyanga Ramasamyc9ecf8e2021-10-08 02:28:52 -0500593 // find for a non printable value in the vector
594 const auto it = std::find_if(vec.begin(), vec.end(),
595 [](const auto& ele) { return !isprint(ele); });
Priyanga Ramasamy02434932021-10-07 16:26:05 -0500596
Priyanga Ramasamyc9ecf8e2021-10-08 02:28:52 -0500597 if (it != vec.end()) // if the given vector has any non printable value
598 {
599 for (auto itr = it; itr != vec.end(); itr++)
Priyanga Ramasamy02434932021-10-07 16:26:05 -0500600 {
Priyanga Ramasamyc9ecf8e2021-10-08 02:28:52 -0500601 if (*itr != 0x00)
602 {
603 str = byteArrayToHexString(vec);
604 return str;
605 }
Priyanga Ramasamy02434932021-10-07 16:26:05 -0500606 }
Priyanga Ramasamyc9ecf8e2021-10-08 02:28:52 -0500607 str = string(vec.begin(), it);
Priyanga Ramasamy02434932021-10-07 16:26:05 -0500608 }
609 else
610 {
611 str = string(vec.begin(), vec.end());
612 }
613 return str;
614}
615
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700616} // namespace vpd
Alpana Kumarif05effd2021-04-07 07:32:53 -0500617} // namespace openpower