blob: 4100d83b23bb0bb514b3cbc86a69ab19666334e2 [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>
12#include <fstream>
13#include <iostream>
14#include <iterator>
15#include <nlohmann/json.hpp>
16
17using namespace std;
18using namespace openpower::vpd;
19using namespace CLI;
20using namespace vpd::keyword::parser;
21using namespace vpdFormat;
22
Santosh Puranik88edeb62020-03-02 12:00:09 +053023/**
24 * @brief Expands location codes
25 */
26static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
27 bool isSystemVpd)
28{
29 auto expanded{unexpanded};
30 static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
31 static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
32 static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
33 size_t idx = expanded.find("fcs");
34 try
35 {
36 if (idx != string::npos)
37 {
38 string fc{};
39 string se{};
40 if (isSystemVpd)
41 {
42 const auto& fcData = vpdMap.at("VCEN").at("FC");
43 const auto& seData = vpdMap.at("VCEN").at("SE");
44 fc = string(fcData.data(), fcData.size());
45 se = string(seData.data(), seData.size());
46 }
47 else
48 {
49 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
50 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
51 }
52
53 // TODO: See if ND1 can be placed in the JSON
54 expanded.replace(idx, 3, fc.substr(0, 4) + ".ND1." + se);
55 }
56 else
57 {
58 idx = expanded.find("mts");
59 if (idx != string::npos)
60 {
61 string mt{};
62 string se{};
63 if (isSystemVpd)
64 {
65 const auto& mtData = vpdMap.at("VSYS").at("TM");
66 const auto& seData = vpdMap.at("VSYS").at("SE");
67 mt = string(mtData.data(), mtData.size());
68 se = string(seData.data(), seData.size());
69 }
70 else
71 {
72 mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
73 se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
74 }
75
76 replace(mt.begin(), mt.end(), '-', '.');
77 expanded.replace(idx, 3, mt + "." + se);
78 }
79 }
80 }
81 catch (std::exception& e)
82 {
83 std::cerr << "Failed to expand location code with exception: "
84 << e.what() << "\n";
85 }
86 return expanded;
87}
88
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053089/**
90 * @brief Populate FRU specific interfaces.
91 *
92 * This is a common method which handles both
93 * ipz and keyword specific interfaces thus,
94 * reducing the code redundancy.
95 * @param[in] map - Reference to the innermost keyword-value map.
96 * @param[in] preIntrStr - Reference to the interface string.
97 * @param[out] interfaces - Reference to interface map.
98 */
99template <typename T>
100static void populateFruSpecificInterfaces(const T& map,
101 const string& preIntrStr,
102 inventory::InterfaceMap& interfaces)
103{
104 inventory::PropertyMap prop;
105
106 for (const auto& kwVal : map)
107 {
108 std::vector<uint8_t> vec(kwVal.second.begin(), kwVal.second.end());
109
110 auto kw = kwVal.first;
111
112 if (kw[0] == '#')
113 {
114 kw = std::string("PD_") + kw[1];
115 }
116 prop.emplace(move(kw), move(vec));
117 }
118
119 interfaces.emplace(preIntrStr, move(prop));
120}
121
122/**
123 * @brief Populate Interfaces.
124 *
125 * This method populates common and extra interfaces to dbus.
126 * @param[in] js - json object
127 * @param[out] interfaces - Reference to interface map
128 * @param[in] vpdMap - Reference to the parsed vpd map.
Santosh Puranik88edeb62020-03-02 12:00:09 +0530129 * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530130 */
131template <typename T>
132static void populateInterfaces(const nlohmann::json& js,
133 inventory::InterfaceMap& interfaces,
Santosh Puranik88edeb62020-03-02 12:00:09 +0530134 const T& vpdMap, bool isSystemVpd)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530135{
136 for (const auto& ifs : js.items())
137 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530138 string inf = ifs.key();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530139 inventory::PropertyMap props;
140
141 for (const auto& itr : ifs.value().items())
142 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530143 const string& busProp = itr.key();
144
Alpana Kumari31970de2020-02-17 06:49:57 -0600145 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530146 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530147 props.emplace(busProp, itr.value().get<bool>());
148 }
149 else if (itr.value().is_string())
150 {
151 if constexpr (std::is_same<T, Parsed>::value)
152 {
153 if (busProp == "LocationCode" &&
154 inf == "com.ibm.ipzvpd.Location")
155 {
156 auto prop = expandLocationCode(
157 itr.value().get<string>(), vpdMap, isSystemVpd);
158 props.emplace(busProp, prop);
159 }
160 else
161 {
162 props.emplace(busProp, itr.value().get<string>());
163 }
164 }
165 else
166 {
167 props.emplace(busProp, itr.value().get<string>());
168 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530169 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600170 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530171 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600172 const string& rec = itr.value().value("recordName", "");
173 const string& kw = itr.value().value("keywordName", "");
174 const string& encoding = itr.value().value("encoding", "");
175
176 if constexpr (std::is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530177 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530178 if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
179 vpdMap.at(rec).count(kw))
Alpana Kumari31970de2020-02-17 06:49:57 -0600180 {
181 auto encoded =
182 encodeKeyword(vpdMap.at(rec).at(kw), encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530183 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600184 }
185 }
186 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
187 {
188 if (!kw.empty() && vpdMap.count(kw))
189 {
190 auto prop =
191 string(vpdMap.at(kw).begin(), vpdMap.at(kw).end());
192 auto encoded = encodeKeyword(prop, encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530193 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600194 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530195 }
196 }
197 }
198 interfaces.emplace(inf, move(props));
199 }
200}
201
202/**
203 * @brief Populate Dbus.
204 *
205 * This method invokes all the populateInterface functions
206 * and notifies PIM about dbus object.
207 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the input.
208 * @param[in] js - Inventory json object
209 * @param[in] filePath - Path of the vpd file
210 * @param[in] preIntrStr - Interface string
211 */
212template <typename T>
213static void populateDbus(const T& vpdMap, nlohmann::json& js,
214 const string& filePath, const string& preIntrStr)
215{
216 inventory::InterfaceMap interfaces;
217 inventory::ObjectMap objects;
218 inventory::PropertyMap prop;
219
220 for (const auto& item : js["frus"][filePath])
221 {
222 const auto& objectPath = item["inventoryPath"];
223 sdbusplus::message::object_path object(objectPath);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530224 auto isSystemVpd = item.value("isSystemVpd", false);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530225 // Populate the VPD keywords and the common interfaces only if we
226 // are asked to inherit that data from the VPD, else only add the
227 // extraInterfaces.
228 if (item.value("inherit", true))
229 {
230 if constexpr (std::is_same<T, Parsed>::value)
231 {
232 // Each record in the VPD becomes an interface and all keyword
233 // within the record are properties under that interface.
234 for (const auto& record : vpdMap)
235 {
236 populateFruSpecificInterfaces(
237 record.second, preIntrStr + record.first, interfaces);
238 }
239 }
240 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
241 {
242 populateFruSpecificInterfaces(vpdMap, preIntrStr, interfaces);
243 }
Santosh Puranik88edeb62020-03-02 12:00:09 +0530244 if (js.find("commonInterfaces") != js.end())
245 {
246 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
247 isSystemVpd);
248 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530249 }
Santosh Puranik0859eb62020-03-16 02:56:29 -0500250 else
251 {
252 // Check if we have been asked to inherit specific record(s)
253 if constexpr (std::is_same<T, Parsed>::value)
254 {
255 if (item.find("copyRecords") != item.end())
256 {
257 for (const auto& record : item["copyRecords"])
258 {
259 const string& recordName = record;
260 if (vpdMap.find(recordName) != vpdMap.end())
261 {
262 populateFruSpecificInterfaces(
263 vpdMap.at(recordName), preIntrStr + recordName,
264 interfaces);
265 }
266 }
267 }
268 }
269 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530270
271 // Populate interfaces and properties that are common to every FRU
272 // and additional interface that might be defined on a per-FRU basis.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530273 if (item.find("extraInterfaces") != item.end())
274 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530275 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
276 isSystemVpd);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530277 }
278 objects.emplace(move(object), move(interfaces));
279 }
280
281 // Notify PIM
282 inventory::callPIM(move(objects));
283}
284
285int main(int argc, char** argv)
286{
287 int rc = 0;
288
289 try
290 {
291 using json = nlohmann::json;
292
293 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
294 "in DBUS"};
295 string file{};
296
297 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
298 ->required()
299 ->check(ExistingFile);
300
301 CLI11_PARSE(app, argc, argv);
302
303 // Make sure that the file path we get is for a supported EEPROM
304 ifstream inventoryJson(INVENTORY_JSON);
305 auto js = json::parse(inventoryJson);
306
307 if ((js.find("frus") == js.end()) ||
308 (js["frus"].find(file) == js["frus"].end()))
309 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530310 cout << "Device path not in JSON, ignoring" << std::endl;
311 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530312 }
313
314 // Open the file in binary mode
315 ifstream vpdFile(file, ios::binary);
316 // Read the content of the binary file into a vector
317 Binary vpdVector((istreambuf_iterator<char>(vpdFile)),
318 istreambuf_iterator<char>());
319
320 vpdType type = vpdTypeCheck(vpdVector);
321
322 switch (type)
323 {
324 case IPZ_VPD:
325 {
326 // Invoking IPZ Vpd Parser
327 auto vpdStore = parse(move(vpdVector));
328 const Parsed& vpdMap = vpdStore.getVpdMap();
329 string preIntrStr = "com.ibm.ipzvpd.";
330 // Write it to the inventory
331 populateDbus(vpdMap, js, file, preIntrStr);
332 }
333 break;
334
335 case KEYWORD_VPD:
336 {
337 // Creating Keyword Vpd Parser Object
338 KeywordVpdParser parserObj(move(vpdVector));
339 // Invoking KW Vpd Parser
340 const auto& kwValMap = parserObj.parseKwVpd();
341 string preIntrStr = "com.ibm.kwvpd.KWVPD";
342 populateDbus(kwValMap, js, file, preIntrStr);
343 }
344 break;
345 default:
346 throw std::runtime_error("Invalid VPD format");
347 }
348 }
349 catch (exception& e)
350 {
351 cerr << e.what() << "\n";
352 rc = -1;
353 }
354
355 return rc;
356}