blob: 3c7304fff80ca7a312b14e0bf81d8ef04a8bf406 [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
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +05309#include <fstream>
10#include <iomanip>
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -050011#include <nlohmann/json.hpp>
SunnySrivastava19849094d4f2020-08-05 09:32:29 -050012#include <phosphor-logging/elog-errors.hpp>
Patrick Venturec83c4dc2018-11-01 16:29:18 -070013#include <phosphor-logging/log.hpp>
PriyangaRamasamy647868e2020-09-08 17:03:19 +053014#include <regex>
Patrick Venturec83c4dc2018-11-01 16:29:18 -070015#include <sdbusplus/server.hpp>
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +053016#include <sstream>
17#include <vector>
SunnySrivastava19849094d4f2020-08-05 09:32:29 -050018#include <xyz/openbmc_project/Common/error.hpp>
Deepak Kodihalli76794492017-02-16 23:48:18 -060019
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +053020using json = nlohmann::json;
21
Deepak Kodihalli76794492017-02-16 23:48:18 -060022namespace openpower
23{
24namespace vpd
25{
SunnySrivastava1984945a02d2020-05-06 01:55:41 -050026using namespace openpower::vpd::constants;
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -050027using namespace inventory;
28using namespace phosphor::logging;
SunnySrivastava19849094d4f2020-08-05 09:32:29 -050029using namespace sdbusplus::xyz::openbmc_project::Common::Error;
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +053030using namespace record;
31using namespace openpower::vpd::exceptions;
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -050032using namespace common::utility;
Sunny Srivastava0746eee2021-03-22 13:36:54 -050033using Severity = openpower::vpd::constants::PelSeverity;
34
35// mapping of severity enum to severity interface
36static std::unordered_map<Severity, std::string> sevMap = {
37 {Severity::INFORMATIONAL,
38 "xyz.openbmc_project.Logging.Entry.Level.Informational"},
39 {Severity::DEBUG, "xyz.openbmc_project.Logging.Entry.Level.Debug"},
40 {Severity::NOTICE, "xyz.openbmc_project.Logging.Entry.Level.Notice"},
41 {Severity::WARNING, "xyz.openbmc_project.Logging.Entry.Level.Warning"},
42 {Severity::CRITICAL, "xyz.openbmc_project.Logging.Entry.Level.Critical"},
43 {Severity::EMERGENCY, "xyz.openbmc_project.Logging.Entry.Level.Emergency"},
44 {Severity::ERROR, "xyz.openbmc_project.Logging.Entry.Level.Error"},
45 {Severity::ALERT, "xyz.openbmc_project.Logging.Entry.Level.Alert"}};
46
Deepak Kodihalli76794492017-02-16 23:48:18 -060047namespace inventory
48{
49
SunnySrivastava19849094d4f2020-08-05 09:32:29 -050050MapperResponse
51 getObjectSubtreeForInterfaces(const std::string& root, const int32_t depth,
52 const std::vector<std::string>& interfaces)
53{
54 auto bus = sdbusplus::bus::new_default();
55 auto mapperCall = bus.new_method_call(mapperDestination, mapperObjectPath,
56 mapperInterface, "GetSubTree");
57 mapperCall.append(root);
58 mapperCall.append(depth);
59 mapperCall.append(interfaces);
60
61 MapperResponse result = {};
62
63 try
64 {
65 auto response = bus.call(mapperCall);
66
67 response.read(result);
68 }
69 catch (const sdbusplus::exception::SdBusError& e)
70 {
71 log<level::ERR>("Error in mapper GetSubTree",
72 entry("ERROR=%s", e.what()));
73 }
74
75 return result;
76}
77
Deepak Kodihalli76794492017-02-16 23:48:18 -060078} // namespace inventory
79
SunnySrivastava1984f6d541e2020-02-04 12:50:40 -060080LE2ByteData readUInt16LE(Binary::const_iterator iterator)
81{
82 LE2ByteData lowByte = *iterator;
83 LE2ByteData highByte = *(iterator + 1);
84 lowByte |= (highByte << 8);
85 return lowByte;
86}
87
SunnySrivastava1984d076da82020-03-05 05:33:35 -060088/** @brief Encodes a keyword for D-Bus.
89 */
90string encodeKeyword(const string& kw, const string& encoding)
91{
92 if (encoding == "MAC")
93 {
94 string res{};
95 size_t first = kw[0];
96 res += toHex(first >> 4);
97 res += toHex(first & 0x0f);
98 for (size_t i = 1; i < kw.size(); ++i)
99 {
100 res += ":";
101 res += toHex(kw[i] >> 4);
102 res += toHex(kw[i] & 0x0f);
103 }
104 return res;
105 }
106 else if (encoding == "DATE")
107 {
108 // Date, represent as
109 // <year>-<month>-<day> <hour>:<min>
110 string res{};
111 static constexpr uint8_t skipPrefix = 3;
112
113 auto strItr = kw.begin();
114 advance(strItr, skipPrefix);
115 for_each(strItr, kw.end(), [&res](size_t c) { res += c; });
116
117 res.insert(BD_YEAR_END, 1, '-');
118 res.insert(BD_MONTH_END, 1, '-');
119 res.insert(BD_DAY_END, 1, ' ');
120 res.insert(BD_HOUR_END, 1, ':');
121
122 return res;
123 }
124 else // default to string encoding
125 {
126 return string(kw.begin(), kw.end());
127 }
128}
SunnySrivastava198443306542020-04-01 02:50:20 -0500129
130string readBusProperty(const string& obj, const string& inf, const string& prop)
131{
132 std::string propVal{};
133 std::string object = INVENTORY_PATH + obj;
134 auto bus = sdbusplus::bus::new_default();
135 auto properties = bus.new_method_call(
136 "xyz.openbmc_project.Inventory.Manager", object.c_str(),
137 "org.freedesktop.DBus.Properties", "Get");
138 properties.append(inf);
139 properties.append(prop);
140 auto result = bus.call(properties);
141 if (!result.is_method_error())
142 {
SunnySrivastava1984bca5aaa2020-04-21 05:31:04 -0500143 variant<Binary, string> val;
SunnySrivastava198443306542020-04-01 02:50:20 -0500144 result.read(val);
SunnySrivastava198443306542020-04-01 02:50:20 -0500145 if (auto pVal = get_if<Binary>(&val))
146 {
147 propVal.assign(reinterpret_cast<const char*>(pVal->data()),
148 pVal->size());
149 }
SunnySrivastava1984bca5aaa2020-04-21 05:31:04 -0500150 else if (auto pVal = get_if<string>(&val))
151 {
152 propVal.assign(pVal->data(), pVal->size());
153 }
SunnySrivastava198443306542020-04-01 02:50:20 -0500154 }
155 return propVal;
156}
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500157
158void createPEL(const std::map<std::string, std::string>& additionalData,
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500159 const Severity& sev, const std::string& errIntf)
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500160{
161 try
162 {
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500163 std::string pelSeverity =
164 "xyz.openbmc_project.Logging.Entry.Level.Error";
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500165 auto bus = sdbusplus::bus::new_default();
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500166 auto service = getService(bus, loggerObjectPath, loggerCreateInterface);
167 auto method = bus.new_method_call(service.c_str(), loggerObjectPath,
168 loggerCreateInterface, "Create");
169
Sunny Srivastava0746eee2021-03-22 13:36:54 -0500170 auto itr = sevMap.find(sev);
171 if (itr != sevMap.end())
172 {
173 pelSeverity = itr->second;
174 }
175
176 method.append(errIntf, pelSeverity, additionalData);
SunnySrivastava1984a20be8e2020-08-26 02:00:50 -0500177 auto resp = bus.call(method);
178 }
179 catch (const sdbusplus::exception::SdBusError& e)
180 {
181 throw std::runtime_error(
182 "Error in invoking D-Bus logging create interface to register PEL");
183 }
184}
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +0530185
186inventory::VPDfilepath getVpdFilePath(const string& jsonFile,
187 const std::string& ObjPath)
188{
189 ifstream inventoryJson(jsonFile);
190 const auto& jsonObject = json::parse(inventoryJson);
191 inventory::VPDfilepath filePath{};
192
193 if (jsonObject.find("frus") == jsonObject.end())
194 {
195 throw(VpdJsonException(
196 "Invalid JSON structure - frus{} object not found in ", jsonFile));
197 }
198
199 const nlohmann::json& groupFRUS =
200 jsonObject["frus"].get_ref<const nlohmann::json::object_t&>();
201 for (const auto& itemFRUS : groupFRUS.items())
202 {
203 const std::vector<nlohmann::json>& groupEEPROM =
204 itemFRUS.value().get_ref<const nlohmann::json::array_t&>();
205 for (const auto& itemEEPROM : groupEEPROM)
206 {
207 if (itemEEPROM["inventoryPath"]
208 .get_ref<const nlohmann::json::string_t&>() == ObjPath)
209 {
210 filePath = itemFRUS.key();
211 return filePath;
212 }
213 }
214 }
215
216 return filePath;
217}
218
219bool isPathInJson(const std::string& eepromPath)
220{
221 bool present = false;
222 ifstream inventoryJson(INVENTORY_JSON_SYM_LINK);
223
224 try
225 {
226 auto js = json::parse(inventoryJson);
227 if (js.find("frus") == js.end())
228 {
229 throw(VpdJsonException(
230 "Invalid JSON structure - frus{} object not found in ",
231 INVENTORY_JSON_SYM_LINK));
232 }
233 json fruJson = js["frus"];
234
235 if (fruJson.find(eepromPath) != fruJson.end())
236 {
237 present = true;
238 }
239 }
240 catch (json::parse_error& ex)
241 {
242 throw(VpdJsonException("Json Parsing failed", INVENTORY_JSON_SYM_LINK));
243 }
244 return present;
245}
246
247bool isRecKwInDbusJson(const std::string& recordName,
248 const std::string& keyword)
249{
250 ifstream propertyJson(DBUS_PROP_JSON);
251 json dbusProperty;
252 bool present = false;
253
254 if (propertyJson.is_open())
255 {
256 try
257 {
258 auto dbusPropertyJson = json::parse(propertyJson);
259 if (dbusPropertyJson.find("dbusProperties") ==
260 dbusPropertyJson.end())
261 {
262 throw(VpdJsonException("dbusProperties{} object not found in "
263 "DbusProperties json : ",
264 DBUS_PROP_JSON));
265 }
266
267 dbusProperty = dbusPropertyJson["dbusProperties"];
268 if (dbusProperty.contains(recordName))
269 {
270 const vector<string>& kwdsToPublish = dbusProperty[recordName];
271 if (find(kwdsToPublish.begin(), kwdsToPublish.end(), keyword) !=
272 kwdsToPublish.end()) // present
273 {
274 present = true;
275 }
276 }
277 }
278 catch (json::parse_error& ex)
279 {
280 throw(VpdJsonException("Json Parsing failed", DBUS_PROP_JSON));
281 }
282 }
283 else
284 {
285 // If dbus properties json is not available, we assume the given
286 // record-keyword is part of dbus-properties json. So setting the bool
287 // variable to true.
288 present = true;
289 }
290 return present;
291}
292
Sunny Srivastava6c71c9d2021-04-15 04:43:54 -0500293vpdType vpdTypeCheck(const Binary& vpdVector)
294{
295 // Read first 3 Bytes to check the 11S bar code format
296 std::string is11SFormat = "";
297 for (uint8_t i = 0; i < FORMAT_11S_LEN; i++)
298 {
299 is11SFormat += vpdVector[MEMORY_VPD_DATA_START + i];
300 }
301
302 if (vpdVector[IPZ_DATA_START] == KW_VAL_PAIR_START_TAG)
303 {
304 // IPZ VPD FORMAT
305 return vpdType::IPZ_VPD;
306 }
307 else if (vpdVector[KW_VPD_DATA_START] == KW_VPD_START_TAG)
308 {
309 // KEYWORD VPD FORMAT
310 return vpdType::KEYWORD_VPD;
311 }
312 else if (is11SFormat.compare(MEMORY_VPD_START_TAG) == 0)
313 {
314 // Memory VPD format
315 return vpdType::MEMORY_VPD;
316 }
317
318 // INVALID VPD FORMAT
319 return vpdType::INVALID_VPD_FORMAT;
320}
321
Alpana Kumarif05effd2021-04-07 07:32:53 -0500322const string getIM(const Parsed& vpdMap)
323{
324 Binary imVal;
325 auto property = vpdMap.find("VSBP");
326 if (property != vpdMap.end())
327 {
328 auto kw = (property->second).find("IM");
329 if (kw != (property->second).end())
330 {
331 copy(kw->second.begin(), kw->second.end(), back_inserter(imVal));
332 }
333 }
334
335 ostringstream oss;
336 for (auto& i : imVal)
337 {
338 oss << setw(2) << setfill('0') << hex << static_cast<int>(i);
339 }
340
341 return oss.str();
342}
343
344const string getHW(const Parsed& vpdMap)
345{
346 Binary hwVal;
347 auto prop = vpdMap.find("VINI");
348 if (prop != vpdMap.end())
349 {
350 auto kw = (prop->second).find("HW");
351 if (kw != (prop->second).end())
352 {
353 copy(kw->second.begin(), kw->second.end(), back_inserter(hwVal));
354 }
355 }
356
357 ostringstream hwString;
358 for (auto& i : hwVal)
359 {
360 hwString << setw(2) << setfill('0') << hex << static_cast<int>(i);
361 }
362
363 return hwString.str();
364}
365
366string getSystemsJson(const Parsed& vpdMap)
367{
368 string jsonPath = "/usr/share/vpd/";
369 string jsonName{};
370
371 ifstream systemJson(SYSTEM_JSON);
372 if (!systemJson)
373 {
374 throw((VpdJsonException("Failed to access Json path", SYSTEM_JSON)));
375 }
376
377 try
378 {
379 auto js = json::parse(systemJson);
380
381 const string hwKeyword = getHW(vpdMap);
382 const string imKeyword = getIM(vpdMap);
383
384 if (js.find("system") == js.end())
385 {
386 throw runtime_error("Invalid systems Json");
387 }
388
389 if (js["system"].find(imKeyword) == js["system"].end())
390 {
391 throw runtime_error(
392 "Invalid system. This system type is not present "
393 "in the systemsJson. IM: " +
394 imKeyword);
395 }
396
397 if ((js["system"][imKeyword].find("constraint") !=
398 js["system"][imKeyword].end()) &&
399 (hwKeyword == js["system"][imKeyword]["constraint"]["HW"]))
400 {
401 jsonName = js["system"][imKeyword]["constraint"]["json"];
402 }
403 else if (js["system"][imKeyword].find("default") !=
404 js["system"][imKeyword].end())
405 {
406 jsonName = js["system"][imKeyword]["default"];
407 }
408 else
409 {
410 throw runtime_error(
411 "Bad System json. Neither constraint nor default found");
412 }
413
414 jsonPath += jsonName;
415 }
416
417 catch (json::parse_error& ex)
418 {
419 throw(VpdJsonException("Json Parsing failed", SYSTEM_JSON));
420 }
421 return jsonPath;
422}
423
PriyangaRamasamy647868e2020-09-08 17:03:19 +0530424void udevToGenericPath(string& file)
425{
426 // Sample udevEvent i2c path :
427 // "/sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a480.i2c-bus/i2c-8/8-0051/8-00510/nvmem"
428 // find if the path contains the word i2c in it.
429 if (file.find("i2c") != string::npos)
430 {
431 string i2cBusAddr{};
432
433 // Every udev i2c path should have the common pattern
434 // "i2c-bus_number/bus_number-vpd_address". Search for
435 // "bus_number-vpd_address".
436 regex i2cPattern("((i2c)-[0-9]+\\/)([0-9]+-[0-9]{4})");
437 smatch match;
438 if (regex_search(file, match, i2cPattern))
439 {
440 i2cBusAddr = match.str(3);
441 }
442 else
443 {
444 cerr << "The given udev path < " << file
445 << " > doesn't match the required pattern. Skipping VPD "
446 "collection."
447 << endl;
448 exit(EXIT_SUCCESS);
449 }
450 // Forming the generic file path
451 file = i2cPathPrefix + i2cBusAddr + "/eeprom";
452 }
453 // Sample udevEvent spi path :
454 // "/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"
455 // find if the path contains the word spi in it.
456 else if (file.find("spi") != string::npos)
457 {
458 // Every udev spi path will have common pattern "spi<Digit>/", which
459 // describes the spi bus number at which the fru is connected; Followed
460 // by a slash following the vpd address of the fru. Taking the above
461 // input as a common key, we try to search for the pattern "spi<Digit>/"
462 // using regular expression.
463 regex spiPattern("((spi)[0-9]+)(\\/)");
464 string spiBus{};
465 smatch match;
466 if (regex_search(file, match, spiPattern))
467 {
468 spiBus = match.str(1);
469 }
470 else
471 {
472 cerr << "The given udev path < " << file
473 << " > doesn't match the required pattern. Skipping VPD "
474 "collection."
475 << endl;
476 exit(EXIT_SUCCESS);
477 }
478 // Forming the generic path
479 file = spiPathPrefix + spiBus + ".0/eeprom";
480 }
481 else
482 {
483 cerr << "\n The given EEPROM path < " << file
484 << " > is not valid. It's neither I2C nor "
485 "SPI path. Skipping VPD collection.."
486 << endl;
487 exit(EXIT_SUCCESS);
488 }
489}
Patrick Venturec83c4dc2018-11-01 16:29:18 -0700490} // namespace vpd
Alpana Kumarif05effd2021-04-07 07:32:53 -0500491} // namespace openpower