blob: 9022e3dc82778b48504ec53eb059e244f5999262 [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
9#include <CLI/CLI.hpp>
Santosh Puranik88edeb62020-03-02 12:00:09 +053010#include <algorithm>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053011#include <exception>
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053012#include <filesystem>
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053013#include <fstream>
14#include <iostream>
15#include <iterator>
16#include <nlohmann/json.hpp>
17
18using namespace std;
19using namespace openpower::vpd;
20using namespace CLI;
21using namespace vpd::keyword::parser;
22using namespace vpdFormat;
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +053023using namespace openpower::vpd::constants;
24namespace fs = filesystem;
25using json = nlohmann::json;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053026
Santosh Puranik88edeb62020-03-02 12:00:09 +053027/**
28 * @brief Expands location codes
29 */
30static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
31 bool isSystemVpd)
32{
33 auto expanded{unexpanded};
34 static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
35 static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
36 static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
37 size_t idx = expanded.find("fcs");
38 try
39 {
40 if (idx != string::npos)
41 {
42 string fc{};
43 string se{};
44 if (isSystemVpd)
45 {
46 const auto& fcData = vpdMap.at("VCEN").at("FC");
47 const auto& seData = vpdMap.at("VCEN").at("SE");
48 fc = string(fcData.data(), fcData.size());
49 se = string(seData.data(), seData.size());
50 }
51 else
52 {
53 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
54 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
55 }
56
57 // TODO: See if ND1 can be placed in the JSON
58 expanded.replace(idx, 3, fc.substr(0, 4) + ".ND1." + se);
59 }
60 else
61 {
62 idx = expanded.find("mts");
63 if (idx != string::npos)
64 {
65 string mt{};
66 string se{};
67 if (isSystemVpd)
68 {
69 const auto& mtData = vpdMap.at("VSYS").at("TM");
70 const auto& seData = vpdMap.at("VSYS").at("SE");
71 mt = string(mtData.data(), mtData.size());
72 se = string(seData.data(), seData.size());
73 }
74 else
75 {
76 mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
77 se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
78 }
79
80 replace(mt.begin(), mt.end(), '-', '.');
81 expanded.replace(idx, 3, mt + "." + se);
82 }
83 }
84 }
85 catch (std::exception& e)
86 {
87 std::cerr << "Failed to expand location code with exception: "
88 << e.what() << "\n";
89 }
90 return expanded;
91}
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053092/**
93 * @brief Populate FRU specific interfaces.
94 *
95 * This is a common method which handles both
96 * ipz and keyword specific interfaces thus,
97 * reducing the code redundancy.
98 * @param[in] map - Reference to the innermost keyword-value map.
99 * @param[in] preIntrStr - Reference to the interface string.
100 * @param[out] interfaces - Reference to interface map.
101 */
102template <typename T>
103static void populateFruSpecificInterfaces(const T& map,
104 const string& preIntrStr,
105 inventory::InterfaceMap& interfaces)
106{
107 inventory::PropertyMap prop;
108
109 for (const auto& kwVal : map)
110 {
111 std::vector<uint8_t> vec(kwVal.second.begin(), kwVal.second.end());
112
113 auto kw = kwVal.first;
114
115 if (kw[0] == '#')
116 {
117 kw = std::string("PD_") + kw[1];
118 }
119 prop.emplace(move(kw), move(vec));
120 }
121
122 interfaces.emplace(preIntrStr, move(prop));
123}
124
125/**
126 * @brief Populate Interfaces.
127 *
128 * This method populates common and extra interfaces to dbus.
129 * @param[in] js - json object
130 * @param[out] interfaces - Reference to interface map
131 * @param[in] vpdMap - Reference to the parsed vpd map.
Santosh Puranik88edeb62020-03-02 12:00:09 +0530132 * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530133 */
134template <typename T>
135static void populateInterfaces(const nlohmann::json& js,
136 inventory::InterfaceMap& interfaces,
Santosh Puranik88edeb62020-03-02 12:00:09 +0530137 const T& vpdMap, bool isSystemVpd)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530138{
139 for (const auto& ifs : js.items())
140 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530141 string inf = ifs.key();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530142 inventory::PropertyMap props;
143
144 for (const auto& itr : ifs.value().items())
145 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530146 const string& busProp = itr.key();
147
Alpana Kumari31970de2020-02-17 06:49:57 -0600148 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530149 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530150 props.emplace(busProp, itr.value().get<bool>());
151 }
152 else if (itr.value().is_string())
153 {
154 if constexpr (std::is_same<T, Parsed>::value)
155 {
156 if (busProp == "LocationCode" &&
157 inf == "com.ibm.ipzvpd.Location")
158 {
159 auto prop = expandLocationCode(
160 itr.value().get<string>(), vpdMap, isSystemVpd);
161 props.emplace(busProp, prop);
162 }
163 else
164 {
165 props.emplace(busProp, itr.value().get<string>());
166 }
167 }
168 else
169 {
170 props.emplace(busProp, itr.value().get<string>());
171 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530172 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600173 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530174 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600175 const string& rec = itr.value().value("recordName", "");
176 const string& kw = itr.value().value("keywordName", "");
177 const string& encoding = itr.value().value("encoding", "");
178
179 if constexpr (std::is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530180 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530181 if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
182 vpdMap.at(rec).count(kw))
Alpana Kumari31970de2020-02-17 06:49:57 -0600183 {
184 auto encoded =
185 encodeKeyword(vpdMap.at(rec).at(kw), encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530186 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600187 }
188 }
189 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
190 {
191 if (!kw.empty() && vpdMap.count(kw))
192 {
193 auto prop =
194 string(vpdMap.at(kw).begin(), vpdMap.at(kw).end());
195 auto encoded = encodeKeyword(prop, encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530196 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600197 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530198 }
199 }
200 }
201 interfaces.emplace(inf, move(props));
202 }
203}
204
205/**
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530206 * @brief Prime the Inventory
207 * Prime the inventory by populating only the location code,
208 * type interface and the inventory object for the frus
209 * which are not system vpd fru.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530210 *
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530211 * @param[in] jsObject - Reference to vpd inventory json object
212 * @param[in] vpdMap - Reference to the parsed vpd map
213 *
214 * @returns Map of items in extraInterface.
215 */
216template <typename T>
217inventory::ObjectMap primeInventory(const nlohmann::json& jsObject,
218 const T& vpdMap)
219{
220 inventory::ObjectMap objects;
221
222 for (auto& itemFRUS : jsObject["frus"].items())
223 {
224 for (auto& itemEEPROM : itemFRUS.value())
225 {
226 inventory::InterfaceMap interfaces;
227 auto isSystemVpd = itemEEPROM.value("isSystemVpd", false);
228 inventory::Object object(itemEEPROM.at("inventoryPath"));
229
230 if (!isSystemVpd && !itemEEPROM.value("noprime", false))
231 {
232 if (itemEEPROM.find("extraInterfaces") != itemEEPROM.end())
233 {
234 for (const auto& eI : itemEEPROM["extraInterfaces"].items())
235 {
236 inventory::PropertyMap props;
237 if (eI.key() ==
238 openpower::vpd::constants::LOCATION_CODE_INF)
239 {
240 if constexpr (std::is_same<T, Parsed>::value)
241 {
242 for (auto& lC : eI.value().items())
243 {
244 auto propVal = expandLocationCode(
245 lC.value().get<string>(), vpdMap, true);
246
247 props.emplace(move(lC.key()),
248 move(propVal));
249 interfaces.emplace(move(eI.key()),
250 move(props));
251 }
252 }
253 }
254 else if (eI.key().find("Inventory.Item.") !=
255 string::npos)
256 {
257 interfaces.emplace(move(eI.key()), move(props));
258 }
259 }
260 }
261 objects.emplace(move(object), move(interfaces));
262 }
263 }
264 }
265 return objects;
266}
267
268/**
269 * @brief Populate Dbus.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530270 * This method invokes all the populateInterface functions
271 * and notifies PIM about dbus object.
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530272 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the
273 * input.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530274 * @param[in] js - Inventory json object
275 * @param[in] filePath - Path of the vpd file
276 * @param[in] preIntrStr - Interface string
277 */
278template <typename T>
279static void populateDbus(const T& vpdMap, nlohmann::json& js,
280 const string& filePath, const string& preIntrStr)
281{
282 inventory::InterfaceMap interfaces;
283 inventory::ObjectMap objects;
284 inventory::PropertyMap prop;
285
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530286 bool isSystemVpd;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530287 for (const auto& item : js["frus"][filePath])
288 {
289 const auto& objectPath = item["inventoryPath"];
290 sdbusplus::message::object_path object(objectPath);
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530291 isSystemVpd = item.value("isSystemVpd", false);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530292 // Populate the VPD keywords and the common interfaces only if we
293 // are asked to inherit that data from the VPD, else only add the
294 // extraInterfaces.
295 if (item.value("inherit", true))
296 {
297 if constexpr (std::is_same<T, Parsed>::value)
298 {
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530299 // Each record in the VPD becomes an interface and all
300 // keyword within the record are properties under that
301 // interface.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530302 for (const auto& record : vpdMap)
303 {
304 populateFruSpecificInterfaces(
305 record.second, preIntrStr + record.first, interfaces);
306 }
307 }
308 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
309 {
310 populateFruSpecificInterfaces(vpdMap, preIntrStr, interfaces);
311 }
Santosh Puranik88edeb62020-03-02 12:00:09 +0530312 if (js.find("commonInterfaces") != js.end())
313 {
314 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
315 isSystemVpd);
316 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530317 }
Santosh Puranik0859eb62020-03-16 02:56:29 -0500318 else
319 {
320 // Check if we have been asked to inherit specific record(s)
321 if constexpr (std::is_same<T, Parsed>::value)
322 {
323 if (item.find("copyRecords") != item.end())
324 {
325 for (const auto& record : item["copyRecords"])
326 {
327 const string& recordName = record;
328 if (vpdMap.find(recordName) != vpdMap.end())
329 {
330 populateFruSpecificInterfaces(
331 vpdMap.at(recordName), preIntrStr + recordName,
332 interfaces);
333 }
334 }
335 }
336 }
337 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530338
339 // Populate interfaces and properties that are common to every FRU
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530340 // and additional interface that might be defined on a per-FRU
341 // basis.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530342 if (item.find("extraInterfaces") != item.end())
343 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530344 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
345 isSystemVpd);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530346 }
347 objects.emplace(move(object), move(interfaces));
348 }
349
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530350 if (isSystemVpd)
351 {
PriyangaRamasamy83a1d5d2020-04-30 19:15:43 +0530352 vector<uint8_t> imVal;
353 if constexpr (is_same<T, Parsed>::value)
354 {
355 auto property = vpdMap.find("VSBP");
356 if (property != vpdMap.end())
357 {
358 auto value = (property->second).find("IM");
359 if (value != (property->second).end())
360 {
361 copy(value->second.begin(), value->second.end(),
362 back_inserter(imVal));
363 }
364 }
365 }
366
367 fs::path target;
368 fs::path link = INVENTORY_JSON_SYM_LINK;
369
370 ostringstream oss;
371 for (auto& i : imVal)
372 {
373 oss << setw(2) << setfill('0') << hex << static_cast<int>(i);
374 }
375 string imValStr = oss.str();
376
377 if (imValStr == SYSTEM_4U) // 4U
378 {
379 target = INVENTORY_JSON_4U;
380 }
381
382 else if (imValStr == SYSTEM_2U) // 2U
383 {
384 target = INVENTORY_JSON_2U;
385 }
386
387 // unlink the symlink which is created at build time
388 remove(INVENTORY_JSON_SYM_LINK);
389 // create a new symlink based on the system
390 fs::create_symlink(target, link);
391
392 // Reloading the json
393 ifstream inventoryJson(link);
394 auto js = json::parse(inventoryJson);
395 inventoryJson.close();
396
PriyangaRamasamy8e140a12020-04-13 19:24:03 +0530397 inventory::ObjectMap primeObject = primeInventory(js, vpdMap);
398 objects.insert(primeObject.begin(), primeObject.end());
399 }
400
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530401 // Notify PIM
402 inventory::callPIM(move(objects));
403}
404
405int main(int argc, char** argv)
406{
407 int rc = 0;
408
409 try
410 {
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530411 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
412 "in DBUS"};
413 string file{};
414
415 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
416 ->required()
417 ->check(ExistingFile);
418
419 CLI11_PARSE(app, argc, argv);
420
421 // Make sure that the file path we get is for a supported EEPROM
422 ifstream inventoryJson(INVENTORY_JSON);
423 auto js = json::parse(inventoryJson);
424
425 if ((js.find("frus") == js.end()) ||
426 (js["frus"].find(file) == js["frus"].end()))
427 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530428 cout << "Device path not in JSON, ignoring" << std::endl;
429 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530430 }
431
432 // Open the file in binary mode
433 ifstream vpdFile(file, ios::binary);
434 // Read the content of the binary file into a vector
435 Binary vpdVector((istreambuf_iterator<char>(vpdFile)),
436 istreambuf_iterator<char>());
437
438 vpdType type = vpdTypeCheck(vpdVector);
439
440 switch (type)
441 {
442 case IPZ_VPD:
443 {
444 // Invoking IPZ Vpd Parser
445 auto vpdStore = parse(move(vpdVector));
446 const Parsed& vpdMap = vpdStore.getVpdMap();
447 string preIntrStr = "com.ibm.ipzvpd.";
448 // Write it to the inventory
449 populateDbus(vpdMap, js, file, preIntrStr);
450 }
451 break;
452
453 case KEYWORD_VPD:
454 {
455 // Creating Keyword Vpd Parser Object
456 KeywordVpdParser parserObj(move(vpdVector));
457 // Invoking KW Vpd Parser
458 const auto& kwValMap = parserObj.parseKwVpd();
459 string preIntrStr = "com.ibm.kwvpd.KWVPD";
460 populateDbus(kwValMap, js, file, preIntrStr);
461 }
462 break;
463 default:
464 throw std::runtime_error("Invalid VPD format");
465 }
466 }
467 catch (exception& e)
468 {
469 cerr << e.what() << "\n";
470 rc = -1;
471 }
472
473 return rc;
474}