blob: b761b1007da766c302ce6a9826eb644f6785dd2f [file] [log] [blame]
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +05301#include "config.h"
2
3#include "defines.hpp"
4#include "ibm_vpd_type_check.hpp"
5#include "keyword_vpd_parser.hpp"
6#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;
24using namespace vpdFormat;
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053025using namespace openpower::vpd::constants;
26namespace fs = filesystem;
27using json = nlohmann::json;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053028
Santosh Puranik88edeb62020-03-02 12:00:09 +053029/**
30 * @brief Expands location codes
31 */
32static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
33 bool isSystemVpd)
34{
35 auto expanded{unexpanded};
36 static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
37 static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
38 static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
39 size_t idx = expanded.find("fcs");
40 try
41 {
42 if (idx != string::npos)
43 {
44 string fc{};
45 string se{};
46 if (isSystemVpd)
47 {
48 const auto& fcData = vpdMap.at("VCEN").at("FC");
49 const auto& seData = vpdMap.at("VCEN").at("SE");
50 fc = string(fcData.data(), fcData.size());
51 se = string(seData.data(), seData.size());
52 }
53 else
54 {
55 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
56 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
57 }
58
59 // TODO: See if ND1 can be placed in the JSON
60 expanded.replace(idx, 3, fc.substr(0, 4) + ".ND1." + se);
61 }
62 else
63 {
64 idx = expanded.find("mts");
65 if (idx != string::npos)
66 {
67 string mt{};
68 string se{};
69 if (isSystemVpd)
70 {
71 const auto& mtData = vpdMap.at("VSYS").at("TM");
72 const auto& seData = vpdMap.at("VSYS").at("SE");
73 mt = string(mtData.data(), mtData.size());
74 se = string(seData.data(), seData.size());
75 }
76 else
77 {
78 mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
79 se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
80 }
81
82 replace(mt.begin(), mt.end(), '-', '.');
83 expanded.replace(idx, 3, mt + "." + se);
84 }
85 }
86 }
87 catch (std::exception& e)
88 {
89 std::cerr << "Failed to expand location code with exception: "
90 << e.what() << "\n";
91 }
92 return expanded;
93}
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053094/**
95 * @brief Populate FRU specific interfaces.
96 *
97 * This is a common method which handles both
98 * ipz and keyword specific interfaces thus,
99 * reducing the code redundancy.
100 * @param[in] map - Reference to the innermost keyword-value map.
101 * @param[in] preIntrStr - Reference to the interface string.
102 * @param[out] interfaces - Reference to interface map.
103 */
104template <typename T>
105static void populateFruSpecificInterfaces(const T& map,
106 const string& preIntrStr,
107 inventory::InterfaceMap& interfaces)
108{
109 inventory::PropertyMap prop;
110
111 for (const auto& kwVal : map)
112 {
113 std::vector<uint8_t> vec(kwVal.second.begin(), kwVal.second.end());
114
115 auto kw = kwVal.first;
116
117 if (kw[0] == '#')
118 {
119 kw = std::string("PD_") + kw[1];
120 }
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500121 else if (isdigit(kw[0]))
122 {
123 kw = std::string("N_") + kw;
124 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530125 prop.emplace(move(kw), move(vec));
126 }
127
128 interfaces.emplace(preIntrStr, move(prop));
129}
130
131/**
132 * @brief Populate Interfaces.
133 *
134 * This method populates common and extra interfaces to dbus.
135 * @param[in] js - json object
136 * @param[out] interfaces - Reference to interface map
137 * @param[in] vpdMap - Reference to the parsed vpd map.
Santosh Puranik88edeb62020-03-02 12:00:09 +0530138 * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530139 */
140template <typename T>
141static void populateInterfaces(const nlohmann::json& js,
142 inventory::InterfaceMap& interfaces,
Santosh Puranik88edeb62020-03-02 12:00:09 +0530143 const T& vpdMap, bool isSystemVpd)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530144{
145 for (const auto& ifs : js.items())
146 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530147 string inf = ifs.key();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530148 inventory::PropertyMap props;
149
150 for (const auto& itr : ifs.value().items())
151 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530152 const string& busProp = itr.key();
153
Alpana Kumari31970de2020-02-17 06:49:57 -0600154 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530155 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530156 props.emplace(busProp, itr.value().get<bool>());
157 }
158 else if (itr.value().is_string())
159 {
160 if constexpr (std::is_same<T, Parsed>::value)
161 {
162 if (busProp == "LocationCode" &&
163 inf == "com.ibm.ipzvpd.Location")
164 {
165 auto prop = expandLocationCode(
166 itr.value().get<string>(), vpdMap, isSystemVpd);
167 props.emplace(busProp, prop);
168 }
169 else
170 {
171 props.emplace(busProp, itr.value().get<string>());
172 }
173 }
174 else
175 {
176 props.emplace(busProp, itr.value().get<string>());
177 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530178 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600179 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530180 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600181 const string& rec = itr.value().value("recordName", "");
182 const string& kw = itr.value().value("keywordName", "");
183 const string& encoding = itr.value().value("encoding", "");
184
185 if constexpr (std::is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530186 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530187 if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
188 vpdMap.at(rec).count(kw))
Alpana Kumari31970de2020-02-17 06:49:57 -0600189 {
190 auto encoded =
191 encodeKeyword(vpdMap.at(rec).at(kw), encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530192 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600193 }
194 }
195 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
196 {
197 if (!kw.empty() && vpdMap.count(kw))
198 {
199 auto prop =
200 string(vpdMap.at(kw).begin(), vpdMap.at(kw).end());
201 auto encoded = encodeKeyword(prop, encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530202 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600203 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530204 }
205 }
206 }
207 interfaces.emplace(inf, move(props));
208 }
209}
210
211/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530212 * @brief Prime the Inventory
213 * Prime the inventory by populating only the location code,
214 * type interface and the inventory object for the frus
215 * which are not system vpd fru.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530216 *
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530217 * @param[in] jsObject - Reference to vpd inventory json object
218 * @param[in] vpdMap - Reference to the parsed vpd map
219 *
220 * @returns Map of items in extraInterface.
221 */
222template <typename T>
223inventory::ObjectMap primeInventory(const nlohmann::json& jsObject,
224 const T& vpdMap)
225{
226 inventory::ObjectMap objects;
227
228 for (auto& itemFRUS : jsObject["frus"].items())
229 {
230 for (auto& itemEEPROM : itemFRUS.value())
231 {
232 inventory::InterfaceMap interfaces;
233 auto isSystemVpd = itemEEPROM.value("isSystemVpd", false);
234 inventory::Object object(itemEEPROM.at("inventoryPath"));
235
236 if (!isSystemVpd && !itemEEPROM.value("noprime", false))
237 {
238 if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
239 {
240 for (const auto& eI : itemEEPROM["extraInterfaces"].items())
241 {
242 inventory::PropertyMap props;
243 if (eI.key() ==
244 openpower::vpd::constants::LOCATION_CODE_INF)
245 {
246 if constexpr (std::is_same<T, Parsed>::value)
247 {
248 for (auto& lC : eI.value().items())
249 {
250 auto propVal = expandLocationCode(
251 lC.value().get<string>(), vpdMap, true);
252
253 props.emplace(move(lC.key()),
254 move(propVal));
255 interfaces.emplace(move(eI.key()),
256 move(props));
257 }
258 }
259 }
260 else if (eI.key().find("Inventory.Item.") !=
261 string::npos)
262 {
263 interfaces.emplace(move(eI.key()), move(props));
264 }
265 }
266 }
267 objects.emplace(move(object), move(interfaces));
268 }
269 }
270 }
271 return objects;
272}
273
274/**
275 * @brief Populate Dbus.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530276 * This method invokes all the populateInterface functions
277 * and notifies PIM about dbus object.
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530278 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
279 * input.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530280 * @param[in] js - Inventory json object
281 * @param[in] filePath - Path of the vpd file
282 * @param[in] preIntrStr - Interface string
283 */
284template <typename T>
285static void populateDbus(const T& vpdMap, nlohmann::json& js,
286 const string& filePath, const string& preIntrStr)
287{
288 inventory::InterfaceMap interfaces;
289 inventory::ObjectMap objects;
290 inventory::PropertyMap prop;
291
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530292 bool isSystemVpd;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530293 for (const auto& item : js["frus"][filePath])
294 {
295 const auto& objectPath = item["inventoryPath"];
296 sdbusplus::message::object_path object(objectPath);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530297 isSystemVpd = item.value("isSystemVpd", false);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530298 // Populate the VPD keywords and the common interfaces only if we
299 // are asked to inherit that data from the VPD, else only add the
300 // extraInterfaces.
301 if (item.value("inherit", true))
302 {
303 if constexpr (std::is_same<T, Parsed>::value)
304 {
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530305 // Each record in the VPD becomes an interface and all
306 // keyword within the record are properties under that
307 // interface.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530308 for (const auto& record : vpdMap)
309 {
310 populateFruSpecificInterfaces(
311 record.second, preIntrStr + record.first, interfaces);
312 }
313 }
314 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
315 {
316 populateFruSpecificInterfaces(vpdMap, preIntrStr, interfaces);
317 }
Santosh Puranik88edeb62020-03-02 12:00:09 +0530318 if (js.find("commonInterfaces") != js.end())
319 {
320 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
321 isSystemVpd);
322 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530323 }
Santosh Puranik0859eb62020-03-16 02:56:29 -0500324 else
325 {
326 // Check if we have been asked to inherit specific record(s)
327 if constexpr (std::is_same<T, Parsed>::value)
328 {
329 if (item.find("copyRecords") != item.end())
330 {
331 for (const auto& record : item["copyRecords"])
332 {
333 const string& recordName = record;
334 if (vpdMap.find(recordName) != vpdMap.end())
335 {
336 populateFruSpecificInterfaces(
337 vpdMap.at(recordName), preIntrStr + recordName,
338 interfaces);
339 }
340 }
341 }
342 }
343 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530344
345 // Populate interfaces and properties that are common to every FRU
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530346 // and additional interface that might be defined on a per-FRU
347 // basis.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530348 if (item.find("extraInterfaces") != item.end())
349 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530350 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
351 isSystemVpd);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530352 }
353 objects.emplace(move(object), move(interfaces));
354 }
355
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530356 if (isSystemVpd)
357 {
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +0530358 vector<uint8_t> imVal;
359 if constexpr (is_same<T, Parsed>::value)
360 {
361 auto property = vpdMap.find("VSBP");
362 if (property != vpdMap.end())
363 {
364 auto value = (property->second).find("IM");
365 if (value != (property->second).end())
366 {
367 copy(value->second.begin(), value->second.end(),
368 back_inserter(imVal));
369 }
370 }
371 }
372
373 fs::path target;
374 fs::path link = INVENTORY_JSON_SYM_LINK;
375
376 ostringstream oss;
377 for (auto& i : imVal)
378 {
379 oss << setw(2) << setfill('0') << hex << static_cast<int>(i);
380 }
381 string imValStr = oss.str();
382
383 if (imValStr == SYSTEM_4U) // 4U
384 {
385 target = INVENTORY_JSON_4U;
386 }
387
388 else if (imValStr == SYSTEM_2U) // 2U
389 {
390 target = INVENTORY_JSON_2U;
391 }
392
393 // unlink the symlink which is created at build time
394 remove(INVENTORY_JSON_SYM_LINK);
395 // create a new symlink based on the system
396 fs::create_symlink(target, link);
397
398 // Reloading the json
399 ifstream inventoryJson(link);
400 auto js = json::parse(inventoryJson);
401 inventoryJson.close();
402
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530403 inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
404 objects.insert(primeObject.begin(), primeObject.end());
405 }
406
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530407 // Notify PIM
408 inventory::callPIM(move(objects));
409}
410
411int main(int argc, char** argv)
412{
413 int rc = 0;
414
415 try
416 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530417 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
418 "in DBUS"};
419 string file{};
420
421 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
422 ->required()
423 ->check(ExistingFile);
424
425 CLI11_PARSE(app, argc, argv);
426
427 // Make sure that the file path we get is for a supported EEPROM
428 ifstream inventoryJson(INVENTORY_JSON);
429 auto js = json::parse(inventoryJson);
430
431 if ((js.find("frus") == js.end()) ||
432 (js["frus"].find(file) == js["frus"].end()))
433 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530434 cout << "Device path not in JSON, ignoring" << std::endl;
435 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530436 }
437
Alpana Kumari8ea3f6d2020-04-02 00:26:07 -0500438 uint32_t offset = 0;
439 // check if offset present?
440 for (const auto& item : js["frus"][file])
441 {
442 if (item.find("offset") != item.end())
443 {
444 offset = item["offset"];
445 }
446 }
447
448 // TODO: Figure out a better way to get max possible VPD size.
449 Binary vpdVector;
450 vpdVector.resize(65504);
451 ifstream vpdFile;
452 vpdFile.open(file, ios::binary);
453
454 vpdFile.seekg(offset, ios_base::cur);
455 vpdFile.read(reinterpret_cast<char*>(&vpdVector[0]), 65504);
456 vpdVector.resize(vpdFile.gcount());
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530457
458 vpdType type = vpdTypeCheck(vpdVector);
459
460 switch (type)
461 {
462 case IPZ_VPD:
463 {
464 // Invoking IPZ Vpd Parser
465 auto vpdStore = parse(move(vpdVector));
466 const Parsed& vpdMap = vpdStore.getVpdMap();
467 string preIntrStr = "com.ibm.ipzvpd.";
468 // Write it to the inventory
469 populateDbus(vpdMap, js, file, preIntrStr);
470 }
471 break;
472
473 case KEYWORD_VPD:
474 {
475 // Creating Keyword Vpd Parser Object
476 KeywordVpdParser parserObj(move(vpdVector));
477 // Invoking KW Vpd Parser
478 const auto& kwValMap = parserObj.parseKwVpd();
479 string preIntrStr = "com.ibm.kwvpd.KWVPD";
480 populateDbus(kwValMap, js, file, preIntrStr);
481 }
482 break;
483 default:
484 throw std::runtime_error("Invalid VPD format");
485 }
486 }
487 catch (exception& e)
488 {
489 cerr << e.what() << "\n";
490 rc = -1;
491 }
492
493 return rc;
494}