blob: 24363d0870f68659cf2f00175c63d329acba10f3 [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"
5#include "parser.hpp"
6#include "utils.hpp"
7
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -05008#include <ctype.h>
9
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053010#include <CLI/CLI.hpp>
Santosh Puranik88edeb62020-03-02 12:00:09 +053011#include <algorithm>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053012#include <exception>
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053013#include <filesystem>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053014#include <fstream>
15#include <iostream>
16#include <iterator>
17#include <nlohmann/json.hpp>
18
19using namespace std;
20using namespace openpower::vpd;
21using namespace CLI;
22using namespace vpd::keyword::parser;
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053023using namespace openpower::vpd::constants;
24namespace fs = filesystem;
25using json = nlohmann::json;
SunnySrivastava1984945a02d2020-05-06 01:55:41 -050026using namespace openpower::vpd::inventory;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053027
Santosh Puranik88edeb62020-03-02 12:00:09 +053028/**
29 * @brief Expands location codes
30 */
31static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
32 bool isSystemVpd)
33{
34 auto expanded{unexpanded};
35 static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
36 static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
37 static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
38 size_t idx = expanded.find("fcs");
39 try
40 {
41 if (idx != string::npos)
42 {
43 string fc{};
44 string se{};
45 if (isSystemVpd)
46 {
47 const auto& fcData = vpdMap.at("VCEN").at("FC");
48 const auto& seData = vpdMap.at("VCEN").at("SE");
49 fc = string(fcData.data(), fcData.size());
50 se = string(seData.data(), seData.size());
51 }
52 else
53 {
54 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
55 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
56 }
57
58 // TODO: See if ND1 can be placed in the JSON
59 expanded.replace(idx, 3, fc.substr(0, 4) + ".ND1." + se);
60 }
61 else
62 {
63 idx = expanded.find("mts");
64 if (idx != string::npos)
65 {
66 string mt{};
67 string se{};
68 if (isSystemVpd)
69 {
70 const auto& mtData = vpdMap.at("VSYS").at("TM");
71 const auto& seData = vpdMap.at("VSYS").at("SE");
72 mt = string(mtData.data(), mtData.size());
73 se = string(seData.data(), seData.size());
74 }
75 else
76 {
77 mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
78 se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
79 }
80
81 replace(mt.begin(), mt.end(), '-', '.');
82 expanded.replace(idx, 3, mt + "." + se);
83 }
84 }
85 }
86 catch (std::exception& e)
87 {
88 std::cerr << "Failed to expand location code with exception: "
89 << e.what() << "\n";
90 }
91 return expanded;
92}
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053093/**
94 * @brief Populate FRU specific interfaces.
95 *
96 * This is a common method which handles both
97 * ipz and keyword specific interfaces thus,
98 * reducing the code redundancy.
99 * @param[in] map - Reference to the innermost keyword-value map.
100 * @param[in] preIntrStr - Reference to the interface string.
101 * @param[out] interfaces - Reference to interface map.
102 */
103template <typename T>
104static void populateFruSpecificInterfaces(const T& map,
105 const string& preIntrStr,
106 inventory::InterfaceMap& interfaces)
107{
108 inventory::PropertyMap prop;
109
110 for (const auto& kwVal : map)
111 {
112 std::vector<uint8_t> vec(kwVal.second.begin(), kwVal.second.end());
113
114 auto kw = kwVal.first;
115
116 if (kw[0] == '#')
117 {
118 kw = std::string("PD_") + kw[1];
119 }
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500120 else if (isdigit(kw[0]))
121 {
122 kw = std::string("N_") + kw;
123 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530124 prop.emplace(move(kw), move(vec));
125 }
126
127 interfaces.emplace(preIntrStr, move(prop));
128}
129
130/**
131 * @brief Populate Interfaces.
132 *
133 * This method populates common and extra interfaces to dbus.
134 * @param[in] js - json object
135 * @param[out] interfaces - Reference to interface map
136 * @param[in] vpdMap - Reference to the parsed vpd map.
Santosh Puranik88edeb62020-03-02 12:00:09 +0530137 * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530138 */
139template <typename T>
140static void populateInterfaces(const nlohmann::json& js,
141 inventory::InterfaceMap& interfaces,
Santosh Puranik88edeb62020-03-02 12:00:09 +0530142 const T& vpdMap, bool isSystemVpd)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530143{
144 for (const auto& ifs : js.items())
145 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530146 string inf = ifs.key();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530147 inventory::PropertyMap props;
148
149 for (const auto& itr : ifs.value().items())
150 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530151 const string& busProp = itr.key();
152
Alpana Kumari31970de2020-02-17 06:49:57 -0600153 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530154 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530155 props.emplace(busProp, itr.value().get<bool>());
156 }
157 else if (itr.value().is_string())
158 {
159 if constexpr (std::is_same<T, Parsed>::value)
160 {
161 if (busProp == "LocationCode" &&
162 inf == "com.ibm.ipzvpd.Location")
163 {
164 auto prop = expandLocationCode(
165 itr.value().get<string>(), vpdMap, isSystemVpd);
166 props.emplace(busProp, prop);
167 }
168 else
169 {
170 props.emplace(busProp, itr.value().get<string>());
171 }
172 }
173 else
174 {
175 props.emplace(busProp, itr.value().get<string>());
176 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530177 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600178 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530179 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600180 const string& rec = itr.value().value("recordName", "");
181 const string& kw = itr.value().value("keywordName", "");
182 const string& encoding = itr.value().value("encoding", "");
183
184 if constexpr (std::is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530185 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530186 if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
187 vpdMap.at(rec).count(kw))
Alpana Kumari31970de2020-02-17 06:49:57 -0600188 {
189 auto encoded =
190 encodeKeyword(vpdMap.at(rec).at(kw), encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530191 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600192 }
193 }
194 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
195 {
196 if (!kw.empty() && vpdMap.count(kw))
197 {
198 auto prop =
199 string(vpdMap.at(kw).begin(), vpdMap.at(kw).end());
200 auto encoded = encodeKeyword(prop, encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530201 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600202 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530203 }
204 }
205 }
206 interfaces.emplace(inf, move(props));
207 }
208}
209
210/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530211 * @brief Prime the Inventory
212 * Prime the inventory by populating only the location code,
213 * type interface and the inventory object for the frus
214 * which are not system vpd fru.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530215 *
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530216 * @param[in] jsObject - Reference to vpd inventory json object
217 * @param[in] vpdMap - Reference to the parsed vpd map
218 *
219 * @returns Map of items in extraInterface.
220 */
221template <typename T>
222inventory::ObjectMap primeInventory(const nlohmann::json& jsObject,
223 const T& vpdMap)
224{
225 inventory::ObjectMap objects;
226
227 for (auto& itemFRUS : jsObject["frus"].items())
228 {
229 for (auto& itemEEPROM : itemFRUS.value())
230 {
231 inventory::InterfaceMap interfaces;
232 auto isSystemVpd = itemEEPROM.value("isSystemVpd", false);
233 inventory::Object object(itemEEPROM.at("inventoryPath"));
234
235 if (!isSystemVpd && !itemEEPROM.value("noprime", false))
236 {
237 if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
238 {
239 for (const auto& eI : itemEEPROM["extraInterfaces"].items())
240 {
241 inventory::PropertyMap props;
242 if (eI.key() ==
243 openpower::vpd::constants::LOCATION_CODE_INF)
244 {
245 if constexpr (std::is_same<T, Parsed>::value)
246 {
247 for (auto& lC : eI.value().items())
248 {
249 auto propVal = expandLocationCode(
250 lC.value().get<string>(), vpdMap, true);
251
252 props.emplace(move(lC.key()),
253 move(propVal));
254 interfaces.emplace(move(eI.key()),
255 move(props));
256 }
257 }
258 }
259 else if (eI.key().find("Inventory.Item.") !=
260 string::npos)
261 {
262 interfaces.emplace(move(eI.key()), move(props));
263 }
264 }
265 }
266 objects.emplace(move(object), move(interfaces));
267 }
268 }
269 }
270 return objects;
271}
272
273/**
274 * @brief Populate Dbus.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530275 * This method invokes all the populateInterface functions
276 * and notifies PIM about dbus object.
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530277 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
278 * input.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530279 * @param[in] js - Inventory json object
280 * @param[in] filePath - Path of the vpd file
281 * @param[in] preIntrStr - Interface string
282 */
283template <typename T>
284static void populateDbus(const T& vpdMap, nlohmann::json& js,
285 const string& filePath, const string& preIntrStr)
286{
287 inventory::InterfaceMap interfaces;
288 inventory::ObjectMap objects;
289 inventory::PropertyMap prop;
290
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530291 bool isSystemVpd;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530292 for (const auto& item : js["frus"][filePath])
293 {
294 const auto& objectPath = item["inventoryPath"];
295 sdbusplus::message::object_path object(objectPath);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530296 isSystemVpd = item.value("isSystemVpd", false);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530297 // Populate the VPD keywords and the common interfaces only if we
298 // are asked to inherit that data from the VPD, else only add the
299 // extraInterfaces.
300 if (item.value("inherit", true))
301 {
302 if constexpr (std::is_same<T, Parsed>::value)
303 {
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530304 // Each record in the VPD becomes an interface and all
305 // keyword within the record are properties under that
306 // interface.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530307 for (const auto& record : vpdMap)
308 {
309 populateFruSpecificInterfaces(
310 record.second, preIntrStr + record.first, interfaces);
311 }
312 }
313 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
314 {
315 populateFruSpecificInterfaces(vpdMap, preIntrStr, interfaces);
316 }
Santosh Puranik88edeb62020-03-02 12:00:09 +0530317 if (js.find("commonInterfaces") != js.end())
318 {
319 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
320 isSystemVpd);
321 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530322 }
Santosh Puranik0859eb62020-03-16 02:56:29 -0500323 else
324 {
325 // Check if we have been asked to inherit specific record(s)
326 if constexpr (std::is_same<T, Parsed>::value)
327 {
328 if (item.find("copyRecords") != item.end())
329 {
330 for (const auto& record : item["copyRecords"])
331 {
332 const string& recordName = record;
333 if (vpdMap.find(recordName) != vpdMap.end())
334 {
335 populateFruSpecificInterfaces(
336 vpdMap.at(recordName), preIntrStr + recordName,
337 interfaces);
338 }
339 }
340 }
341 }
342 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530343
344 // Populate interfaces and properties that are common to every FRU
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530345 // and additional interface that might be defined on a per-FRU
346 // basis.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530347 if (item.find("extraInterfaces") != item.end())
348 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530349 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
350 isSystemVpd);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530351 }
352 objects.emplace(move(object), move(interfaces));
353 }
354
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530355 if (isSystemVpd)
356 {
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +0530357 vector<uint8_t> imVal;
358 if constexpr (is_same<T, Parsed>::value)
359 {
360 auto property = vpdMap.find("VSBP");
361 if (property != vpdMap.end())
362 {
363 auto value = (property->second).find("IM");
364 if (value != (property->second).end())
365 {
366 copy(value->second.begin(), value->second.end(),
367 back_inserter(imVal));
368 }
369 }
370 }
371
372 fs::path target;
373 fs::path link = INVENTORY_JSON_SYM_LINK;
374
375 ostringstream oss;
376 for (auto& i : imVal)
377 {
378 oss << setw(2) << setfill('0') << hex << static_cast<int>(i);
379 }
380 string imValStr = oss.str();
381
382 if (imValStr == SYSTEM_4U) // 4U
383 {
384 target = INVENTORY_JSON_4U;
385 }
386
387 else if (imValStr == SYSTEM_2U) // 2U
388 {
389 target = INVENTORY_JSON_2U;
390 }
391
392 // unlink the symlink which is created at build time
393 remove(INVENTORY_JSON_SYM_LINK);
394 // create a new symlink based on the system
395 fs::create_symlink(target, link);
396
397 // Reloading the json
398 ifstream inventoryJson(link);
399 auto js = json::parse(inventoryJson);
400 inventoryJson.close();
401
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530402 inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
403 objects.insert(primeObject.begin(), primeObject.end());
404 }
405
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530406 // Notify PIM
407 inventory::callPIM(move(objects));
408}
409
410int main(int argc, char** argv)
411{
412 int rc = 0;
413
414 try
415 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530416 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
417 "in DBUS"};
418 string file{};
419
420 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
421 ->required()
422 ->check(ExistingFile);
423
424 CLI11_PARSE(app, argc, argv);
425
426 // Make sure that the file path we get is for a supported EEPROM
427 ifstream inventoryJson(INVENTORY_JSON);
428 auto js = json::parse(inventoryJson);
429
430 if ((js.find("frus") == js.end()) ||
431 (js["frus"].find(file) == js["frus"].end()))
432 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530433 cout << "Device path not in JSON, ignoring" << std::endl;
434 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530435 }
436
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500437 uint32_t offset = 0;
438 // check if offset present?
439 for (const auto& item : js["frus"][file])
440 {
441 if (item.find("offset") != item.end())
442 {
443 offset = item["offset"];
444 }
445 }
446
447 // TODO: Figure out a better way to get max possible VPD size.
448 Binary vpdVector;
449 vpdVector.resize(65504);
450 ifstream vpdFile;
451 vpdFile.open(file, ios::binary);
452
453 vpdFile.seekg(offset, ios_base::cur);
454 vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), 65504);
455 vpdVector.resize(vpdFile.gcount());
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530456
457 vpdType type = vpdTypeCheck(vpdVector);
458
459 switch (type)
460 {
461 case IPZ_VPD:
462 {
463 // Invoking IPZ Vpd Parser
464 auto vpdStore = parse(move(vpdVector));
465 const Parsed& vpdMap = vpdStore.getVpdMap();
466 string preIntrStr = "com.ibm.ipzvpd.";
467 // Write it to the inventory
468 populateDbus(vpdMap, js, file, preIntrStr);
469 }
470 break;
471
472 case KEYWORD_VPD:
473 {
474 // Creating Keyword Vpd Parser Object
475 KeywordVpdParser parserObj(move(vpdVector));
476 // Invoking KW Vpd Parser
477 const auto& kwValMap = parserObj.parseKwVpd();
478 string preIntrStr = "com.ibm.kwvpd.KWVPD";
479 populateDbus(kwValMap, js, file, preIntrStr);
480 }
481 break;
482 default:
483 throw std::runtime_error("Invalid VPD format");
484 }
485 }
486 catch (exception& e)
487 {
488 cerr << e.what() << "\n";
489 rc = -1;
490 }
491
492 return rc;
493}