blob: ac962953add95007af5b4f4a1ae888ff49a62de4 [file] [log] [blame]
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301#include "config.h"
2
3#include "defines.hpp"
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05304#include "keyword_vpd_parser.hpp"
Alpana Kumaria00936f2020-04-14 07:15:46 -05005#include "memory_vpd_parser.hpp"
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05306#include "parser.hpp"
7#include "utils.hpp"
8
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -05009#include <ctype.h>
10
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053011#include <CLI/CLI.hpp>
Santosh Puranik88edeb62020-03-02 12:00:09 +053012#include <algorithm>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053013#include <exception>
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053014#include <filesystem>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053015#include <fstream>
16#include <iostream>
17#include <iterator>
18#include <nlohmann/json.hpp>
19
20using namespace std;
21using namespace openpower::vpd;
22using namespace CLI;
23using namespace vpd::keyword::parser;
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053024using namespace openpower::vpd::constants;
25namespace fs = filesystem;
26using json = nlohmann::json;
SunnySrivastava1984945a02d2020-05-06 01:55:41 -050027using namespace openpower::vpd::inventory;
Alpana Kumaria00936f2020-04-14 07:15:46 -050028using namespace openpower::vpd::memory::parser;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053029
Santosh Puranik88edeb62020-03-02 12:00:09 +053030/**
31 * @brief Expands location codes
32 */
33static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
34 bool isSystemVpd)
35{
36 auto expanded{unexpanded};
37 static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
38 static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
39 static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
40 size_t idx = expanded.find("fcs");
41 try
42 {
43 if (idx != string::npos)
44 {
45 string fc{};
46 string se{};
47 if (isSystemVpd)
48 {
49 const auto& fcData = vpdMap.at("VCEN").at("FC");
50 const auto& seData = vpdMap.at("VCEN").at("SE");
51 fc = string(fcData.data(), fcData.size());
52 se = string(seData.data(), seData.size());
53 }
54 else
55 {
56 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
57 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
58 }
59
60 // TODO: See if ND1 can be placed in the JSON
61 expanded.replace(idx, 3, fc.substr(0, 4) + ".ND1." + se);
62 }
63 else
64 {
65 idx = expanded.find("mts");
66 if (idx != string::npos)
67 {
68 string mt{};
69 string se{};
70 if (isSystemVpd)
71 {
72 const auto& mtData = vpdMap.at("VSYS").at("TM");
73 const auto& seData = vpdMap.at("VSYS").at("SE");
74 mt = string(mtData.data(), mtData.size());
75 se = string(seData.data(), seData.size());
76 }
77 else
78 {
79 mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
80 se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
81 }
82
83 replace(mt.begin(), mt.end(), '-', '.');
84 expanded.replace(idx, 3, mt + "." + se);
85 }
86 }
87 }
88 catch (std::exception& e)
89 {
90 std::cerr << "Failed to expand location code with exception: "
91 << e.what() << "\n";
92 }
93 return expanded;
94}
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053095/**
96 * @brief Populate FRU specific interfaces.
97 *
98 * This is a common method which handles both
99 * ipz and keyword specific interfaces thus,
100 * reducing the code redundancy.
101 * @param[in] map - Reference to the innermost keyword-value map.
102 * @param[in] preIntrStr - Reference to the interface string.
103 * @param[out] interfaces - Reference to interface map.
104 */
105template <typename T>
106static void populateFruSpecificInterfaces(const T& map,
107 const string& preIntrStr,
108 inventory::InterfaceMap& interfaces)
109{
110 inventory::PropertyMap prop;
111
112 for (const auto& kwVal : map)
113 {
114 std::vector<uint8_t> vec(kwVal.second.begin(), kwVal.second.end());
115
116 auto kw = kwVal.first;
117
118 if (kw[0] == '#')
119 {
120 kw = std::string("PD_") + kw[1];
121 }
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500122 else if (isdigit(kw[0]))
123 {
124 kw = std::string("N_") + kw;
125 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530126 prop.emplace(move(kw), move(vec));
127 }
128
129 interfaces.emplace(preIntrStr, move(prop));
130}
131
132/**
133 * @brief Populate Interfaces.
134 *
135 * This method populates common and extra interfaces to dbus.
136 * @param[in] js - json object
137 * @param[out] interfaces - Reference to interface map
138 * @param[in] vpdMap - Reference to the parsed vpd map.
Santosh Puranik88edeb62020-03-02 12:00:09 +0530139 * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530140 */
141template <typename T>
142static void populateInterfaces(const nlohmann::json& js,
143 inventory::InterfaceMap& interfaces,
Santosh Puranik88edeb62020-03-02 12:00:09 +0530144 const T& vpdMap, bool isSystemVpd)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530145{
146 for (const auto& ifs : js.items())
147 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530148 string inf = ifs.key();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530149 inventory::PropertyMap props;
150
151 for (const auto& itr : ifs.value().items())
152 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530153 const string& busProp = itr.key();
154
Alpana Kumari31970de2020-02-17 06:49:57 -0600155 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530156 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530157 props.emplace(busProp, itr.value().get<bool>());
158 }
159 else if (itr.value().is_string())
160 {
161 if constexpr (std::is_same<T, Parsed>::value)
162 {
163 if (busProp == "LocationCode" &&
164 inf == "com.ibm.ipzvpd.Location")
165 {
166 auto prop = expandLocationCode(
167 itr.value().get<string>(), vpdMap, isSystemVpd);
168 props.emplace(busProp, prop);
169 }
170 else
171 {
172 props.emplace(busProp, itr.value().get<string>());
173 }
174 }
175 else
176 {
177 props.emplace(busProp, itr.value().get<string>());
178 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530179 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600180 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530181 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600182 const string& rec = itr.value().value("recordName", "");
183 const string& kw = itr.value().value("keywordName", "");
184 const string& encoding = itr.value().value("encoding", "");
185
186 if constexpr (std::is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530187 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530188 if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
189 vpdMap.at(rec).count(kw))
Alpana Kumari31970de2020-02-17 06:49:57 -0600190 {
191 auto encoded =
192 encodeKeyword(vpdMap.at(rec).at(kw), encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530193 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600194 }
195 }
196 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
197 {
198 if (!kw.empty() && vpdMap.count(kw))
199 {
200 auto prop =
201 string(vpdMap.at(kw).begin(), vpdMap.at(kw).end());
202 auto encoded = encodeKeyword(prop, encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530203 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600204 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530205 }
206 }
207 }
208 interfaces.emplace(inf, move(props));
209 }
210}
211
212/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530213 * @brief Prime the Inventory
214 * Prime the inventory by populating only the location code,
215 * type interface and the inventory object for the frus
216 * which are not system vpd fru.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530217 *
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530218 * @param[in] jsObject - Reference to vpd inventory json object
219 * @param[in] vpdMap - Reference to the parsed vpd map
220 *
221 * @returns Map of items in extraInterface.
222 */
223template <typename T>
224inventory::ObjectMap primeInventory(const nlohmann::json& jsObject,
225 const T& vpdMap)
226{
227 inventory::ObjectMap objects;
228
229 for (auto& itemFRUS : jsObject["frus"].items())
230 {
231 for (auto& itemEEPROM : itemFRUS.value())
232 {
233 inventory::InterfaceMap interfaces;
234 auto isSystemVpd = itemEEPROM.value("isSystemVpd", false);
235 inventory::Object object(itemEEPROM.at("inventoryPath"));
236
237 if (!isSystemVpd && !itemEEPROM.value("noprime", false))
238 {
239 if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
240 {
241 for (const auto& eI : itemEEPROM["extraInterfaces"].items())
242 {
243 inventory::PropertyMap props;
244 if (eI.key() ==
245 openpower::vpd::constants::LOCATION_CODE_INF)
246 {
247 if constexpr (std::is_same<T, Parsed>::value)
248 {
249 for (auto& lC : eI.value().items())
250 {
251 auto propVal = expandLocationCode(
252 lC.value().get<string>(), vpdMap, true);
253
254 props.emplace(move(lC.key()),
255 move(propVal));
256 interfaces.emplace(move(eI.key()),
257 move(props));
258 }
259 }
260 }
261 else if (eI.key().find("Inventory.Item.") !=
262 string::npos)
263 {
264 interfaces.emplace(move(eI.key()), move(props));
265 }
266 }
267 }
268 objects.emplace(move(object), move(interfaces));
269 }
270 }
271 }
272 return objects;
273}
274
275/**
276 * @brief Populate Dbus.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530277 * This method invokes all the populateInterface functions
278 * and notifies PIM about dbus object.
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530279 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
280 * input.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530281 * @param[in] js - Inventory json object
282 * @param[in] filePath - Path of the vpd file
283 * @param[in] preIntrStr - Interface string
284 */
285template <typename T>
286static void populateDbus(const T& vpdMap, nlohmann::json& js,
287 const string& filePath, const string& preIntrStr)
288{
289 inventory::InterfaceMap interfaces;
290 inventory::ObjectMap objects;
291 inventory::PropertyMap prop;
292
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530293 bool isSystemVpd;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530294 for (const auto& item : js["frus"][filePath])
295 {
296 const auto& objectPath = item["inventoryPath"];
297 sdbusplus::message::object_path object(objectPath);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530298 isSystemVpd = item.value("isSystemVpd", false);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530299 // Populate the VPD keywords and the common interfaces only if we
300 // are asked to inherit that data from the VPD, else only add the
301 // extraInterfaces.
302 if (item.value("inherit", true))
303 {
304 if constexpr (std::is_same<T, Parsed>::value)
305 {
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530306 // Each record in the VPD becomes an interface and all
307 // keyword within the record are properties under that
308 // interface.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530309 for (const auto& record : vpdMap)
310 {
311 populateFruSpecificInterfaces(
312 record.second, preIntrStr + record.first, interfaces);
313 }
314 }
315 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
316 {
317 populateFruSpecificInterfaces(vpdMap, preIntrStr, interfaces);
318 }
Santosh Puranik88edeb62020-03-02 12:00:09 +0530319 if (js.find("commonInterfaces") != js.end())
320 {
321 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
322 isSystemVpd);
323 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530324 }
Santosh Puranik0859eb62020-03-16 02:56:29 -0500325 else
326 {
327 // Check if we have been asked to inherit specific record(s)
328 if constexpr (std::is_same<T, Parsed>::value)
329 {
330 if (item.find("copyRecords") != item.end())
331 {
332 for (const auto& record : item["copyRecords"])
333 {
334 const string& recordName = record;
335 if (vpdMap.find(recordName) != vpdMap.end())
336 {
337 populateFruSpecificInterfaces(
338 vpdMap.at(recordName), preIntrStr + recordName,
339 interfaces);
340 }
341 }
342 }
343 }
344 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530345
346 // Populate interfaces and properties that are common to every FRU
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530347 // and additional interface that might be defined on a per-FRU
348 // basis.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530349 if (item.find("extraInterfaces") != item.end())
350 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530351 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
352 isSystemVpd);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530353 }
354 objects.emplace(move(object), move(interfaces));
355 }
356
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530357 if (isSystemVpd)
358 {
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +0530359 vector<uint8_t> imVal;
360 if constexpr (is_same<T, Parsed>::value)
361 {
362 auto property = vpdMap.find("VSBP");
363 if (property != vpdMap.end())
364 {
365 auto value = (property->second).find("IM");
366 if (value != (property->second).end())
367 {
368 copy(value->second.begin(), value->second.end(),
369 back_inserter(imVal));
370 }
371 }
372 }
373
374 fs::path target;
375 fs::path link = INVENTORY_JSON_SYM_LINK;
376
377 ostringstream oss;
378 for (auto& i : imVal)
379 {
380 oss << setw(2) << setfill('0') << hex << static_cast<int>(i);
381 }
382 string imValStr = oss.str();
383
384 if (imValStr == SYSTEM_4U) // 4U
385 {
386 target = INVENTORY_JSON_4U;
387 }
388
389 else if (imValStr == SYSTEM_2U) // 2U
390 {
391 target = INVENTORY_JSON_2U;
392 }
393
394 // unlink the symlink which is created at build time
395 remove(INVENTORY_JSON_SYM_LINK);
396 // create a new symlink based on the system
397 fs::create_symlink(target, link);
398
399 // Reloading the json
400 ifstream inventoryJson(link);
401 auto js = json::parse(inventoryJson);
402 inventoryJson.close();
403
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530404 inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
405 objects.insert(primeObject.begin(), primeObject.end());
406 }
407
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530408 // Notify PIM
409 inventory::callPIM(move(objects));
410}
411
412int main(int argc, char** argv)
413{
414 int rc = 0;
415
416 try
417 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530418 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
419 "in DBUS"};
420 string file{};
421
422 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
423 ->required()
424 ->check(ExistingFile);
425
426 CLI11_PARSE(app, argc, argv);
427
428 // Make sure that the file path we get is for a supported EEPROM
429 ifstream inventoryJson(INVENTORY_JSON);
430 auto js = json::parse(inventoryJson);
431
432 if ((js.find("frus") == js.end()) ||
433 (js["frus"].find(file) == js["frus"].end()))
434 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530435 cout << "Device path not in JSON, ignoring" << std::endl;
436 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530437 }
438
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500439 uint32_t offset = 0;
440 // check if offset present?
441 for (const auto& item : js["frus"][file])
442 {
443 if (item.find("offset") != item.end())
444 {
445 offset = item["offset"];
446 }
447 }
448
449 // TODO: Figure out a better way to get max possible VPD size.
450 Binary vpdVector;
451 vpdVector.resize(65504);
452 ifstream vpdFile;
453 vpdFile.open(file, ios::binary);
454
455 vpdFile.seekg(offset, ios_base::cur);
456 vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), 65504);
457 vpdVector.resize(vpdFile.gcount());
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530458
459 vpdType type = vpdTypeCheck(vpdVector);
460
461 switch (type)
462 {
463 case IPZ_VPD:
464 {
465 // Invoking IPZ Vpd Parser
466 auto vpdStore = parse(move(vpdVector));
467 const Parsed& vpdMap = vpdStore.getVpdMap();
468 string preIntrStr = "com.ibm.ipzvpd.";
469 // Write it to the inventory
470 populateDbus(vpdMap, js, file, preIntrStr);
471 }
472 break;
473
474 case KEYWORD_VPD:
475 {
476 // Creating Keyword Vpd Parser Object
477 KeywordVpdParser parserObj(move(vpdVector));
478 // Invoking KW Vpd Parser
479 const auto& kwValMap = parserObj.parseKwVpd();
Alpana Kumaria00936f2020-04-14 07:15:46 -0500480 string preIntrStr = "com.ibm.ipzvpd.VINI";
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530481 populateDbus(kwValMap, js, file, preIntrStr);
482 }
483 break;
Alpana Kumaria00936f2020-04-14 07:15:46 -0500484
485 case MEMORY_VPD:
486 {
487 // Get an object to call API & get the key-value map
488 memoryVpdParser vpdParser(move(vpdVector));
489 const auto& memKwValMap = vpdParser.parseMemVpd();
490
491 string preIntrStr = "com.ibm.kwvpd.KWVPD";
492 // js(define dimm sys path in js), ObjPath(define in JS)
493 populateDbus(memKwValMap, js, file, preIntrStr);
494 }
495 break;
496
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530497 default:
498 throw std::runtime_error("Invalid VPD format");
499 }
500 }
501 catch (exception& e)
502 {
503 cerr << e.what() << "\n";
504 rc = -1;
505 }
506
507 return rc;
508}