blob: faec395374bf2d05f8d4ed20324870e59d39619b [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/** @brief Reads a property from the inventory manager given object path,
24 * intreface and property.
25 */
26static auto readBusProperty(const string& obj, const string& inf,
27 const string& prop)
28{
29 string propVal{};
30 static constexpr auto OBJ_PREFIX = "/xyz/openbmc_project/inventory";
31 string object = OBJ_PREFIX + obj;
32 auto bus = sdbusplus::bus::new_default();
33 auto properties = bus.new_method_call(
34 "xyz.openbmc_project.Inventory.Manager", object.c_str(),
35 "org.freedesktop.DBus.Properties", "Get");
36 properties.append(inf);
37 properties.append(prop);
38 auto result = bus.call(properties);
39 if (!result.is_method_error())
40 {
41 variant<Binary> val;
42 result.read(val);
43
44 if (auto pVal = get_if<Binary>(&val))
45 {
46 propVal.assign(reinterpret_cast<const char*>(pVal->data()),
47 pVal->size());
48 }
49 }
50 return propVal;
51}
52
53/**
54 * @brief Expands location codes
55 */
56static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
57 bool isSystemVpd)
58{
59 auto expanded{unexpanded};
60 static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
61 static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
62 static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
63 size_t idx = expanded.find("fcs");
64 try
65 {
66 if (idx != string::npos)
67 {
68 string fc{};
69 string se{};
70 if (isSystemVpd)
71 {
72 const auto& fcData = vpdMap.at("VCEN").at("FC");
73 const auto& seData = vpdMap.at("VCEN").at("SE");
74 fc = string(fcData.data(), fcData.size());
75 se = string(seData.data(), seData.size());
76 }
77 else
78 {
79 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
80 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
81 }
82
83 // TODO: See if ND1 can be placed in the JSON
84 expanded.replace(idx, 3, fc.substr(0, 4) + ".ND1." + se);
85 }
86 else
87 {
88 idx = expanded.find("mts");
89 if (idx != string::npos)
90 {
91 string mt{};
92 string se{};
93 if (isSystemVpd)
94 {
95 const auto& mtData = vpdMap.at("VSYS").at("TM");
96 const auto& seData = vpdMap.at("VSYS").at("SE");
97 mt = string(mtData.data(), mtData.size());
98 se = string(seData.data(), seData.size());
99 }
100 else
101 {
102 mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
103 se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
104 }
105
106 replace(mt.begin(), mt.end(), '-', '.');
107 expanded.replace(idx, 3, mt + "." + se);
108 }
109 }
110 }
111 catch (std::exception& e)
112 {
113 std::cerr << "Failed to expand location code with exception: "
114 << e.what() << "\n";
115 }
116 return expanded;
117}
118
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530119/**
120 * @brief Populate FRU specific interfaces.
121 *
122 * This is a common method which handles both
123 * ipz and keyword specific interfaces thus,
124 * reducing the code redundancy.
125 * @param[in] map - Reference to the innermost keyword-value map.
126 * @param[in] preIntrStr - Reference to the interface string.
127 * @param[out] interfaces - Reference to interface map.
128 */
129template <typename T>
130static void populateFruSpecificInterfaces(const T& map,
131 const string& preIntrStr,
132 inventory::InterfaceMap& interfaces)
133{
134 inventory::PropertyMap prop;
135
136 for (const auto& kwVal : map)
137 {
138 std::vector<uint8_t> vec(kwVal.second.begin(), kwVal.second.end());
139
140 auto kw = kwVal.first;
141
142 if (kw[0] == '#')
143 {
144 kw = std::string("PD_") + kw[1];
145 }
146 prop.emplace(move(kw), move(vec));
147 }
148
149 interfaces.emplace(preIntrStr, move(prop));
150}
151
152/**
153 * @brief Populate Interfaces.
154 *
155 * This method populates common and extra interfaces to dbus.
156 * @param[in] js - json object
157 * @param[out] interfaces - Reference to interface map
158 * @param[in] vpdMap - Reference to the parsed vpd map.
Santosh Puranik88edeb62020-03-02 12:00:09 +0530159 * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530160 */
161template <typename T>
162static void populateInterfaces(const nlohmann::json& js,
163 inventory::InterfaceMap& interfaces,
Santosh Puranik88edeb62020-03-02 12:00:09 +0530164 const T& vpdMap, bool isSystemVpd)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530165{
166 for (const auto& ifs : js.items())
167 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530168 string inf = ifs.key();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530169 inventory::PropertyMap props;
170
171 for (const auto& itr : ifs.value().items())
172 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530173 const string& busProp = itr.key();
174
Alpana Kumari31970de2020-02-17 06:49:57 -0600175 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530176 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530177 props.emplace(busProp, itr.value().get<bool>());
178 }
179 else if (itr.value().is_string())
180 {
181 if constexpr (std::is_same<T, Parsed>::value)
182 {
183 if (busProp == "LocationCode" &&
184 inf == "com.ibm.ipzvpd.Location")
185 {
186 auto prop = expandLocationCode(
187 itr.value().get<string>(), vpdMap, isSystemVpd);
188 props.emplace(busProp, prop);
189 }
190 else
191 {
192 props.emplace(busProp, itr.value().get<string>());
193 }
194 }
195 else
196 {
197 props.emplace(busProp, itr.value().get<string>());
198 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530199 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600200 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530201 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600202 const string& rec = itr.value().value("recordName", "");
203 const string& kw = itr.value().value("keywordName", "");
204 const string& encoding = itr.value().value("encoding", "");
205
206 if constexpr (std::is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530207 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530208 if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
209 vpdMap.at(rec).count(kw))
Alpana Kumari31970de2020-02-17 06:49:57 -0600210 {
211 auto encoded =
212 encodeKeyword(vpdMap.at(rec).at(kw), encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530213 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600214 }
215 }
216 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
217 {
218 if (!kw.empty() && vpdMap.count(kw))
219 {
220 auto prop =
221 string(vpdMap.at(kw).begin(), vpdMap.at(kw).end());
222 auto encoded = encodeKeyword(prop, encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530223 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600224 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530225 }
226 }
227 }
228 interfaces.emplace(inf, move(props));
229 }
230}
231
232/**
233 * @brief Populate Dbus.
234 *
235 * This method invokes all the populateInterface functions
236 * and notifies PIM about dbus object.
237 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the input.
238 * @param[in] js - Inventory json object
239 * @param[in] filePath - Path of the vpd file
240 * @param[in] preIntrStr - Interface string
241 */
242template <typename T>
243static void populateDbus(const T& vpdMap, nlohmann::json& js,
244 const string& filePath, const string& preIntrStr)
245{
246 inventory::InterfaceMap interfaces;
247 inventory::ObjectMap objects;
248 inventory::PropertyMap prop;
249
250 for (const auto& item : js["frus"][filePath])
251 {
252 const auto& objectPath = item["inventoryPath"];
253 sdbusplus::message::object_path object(objectPath);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530254 auto isSystemVpd = item.value("isSystemVpd", false);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530255 // Populate the VPD keywords and the common interfaces only if we
256 // are asked to inherit that data from the VPD, else only add the
257 // extraInterfaces.
258 if (item.value("inherit", true))
259 {
260 if constexpr (std::is_same<T, Parsed>::value)
261 {
262 // Each record in the VPD becomes an interface and all keyword
263 // within the record are properties under that interface.
264 for (const auto& record : vpdMap)
265 {
266 populateFruSpecificInterfaces(
267 record.second, preIntrStr + record.first, interfaces);
268 }
269 }
270 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
271 {
272 populateFruSpecificInterfaces(vpdMap, preIntrStr, interfaces);
273 }
Santosh Puranik88edeb62020-03-02 12:00:09 +0530274 if (js.find("commonInterfaces") != js.end())
275 {
276 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
277 isSystemVpd);
278 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530279 }
Santosh Puranik0859eb62020-03-16 02:56:29 -0500280 else
281 {
282 // Check if we have been asked to inherit specific record(s)
283 if constexpr (std::is_same<T, Parsed>::value)
284 {
285 if (item.find("copyRecords") != item.end())
286 {
287 for (const auto& record : item["copyRecords"])
288 {
289 const string& recordName = record;
290 if (vpdMap.find(recordName) != vpdMap.end())
291 {
292 populateFruSpecificInterfaces(
293 vpdMap.at(recordName), preIntrStr + recordName,
294 interfaces);
295 }
296 }
297 }
298 }
299 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530300
301 // Populate interfaces and properties that are common to every FRU
302 // and additional interface that might be defined on a per-FRU basis.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530303 if (item.find("extraInterfaces") != item.end())
304 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530305 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
306 isSystemVpd);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530307 }
308 objects.emplace(move(object), move(interfaces));
309 }
310
311 // Notify PIM
312 inventory::callPIM(move(objects));
313}
314
315int main(int argc, char** argv)
316{
317 int rc = 0;
318
319 try
320 {
321 using json = nlohmann::json;
322
323 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
324 "in DBUS"};
325 string file{};
326
327 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
328 ->required()
329 ->check(ExistingFile);
330
331 CLI11_PARSE(app, argc, argv);
332
333 // Make sure that the file path we get is for a supported EEPROM
334 ifstream inventoryJson(INVENTORY_JSON);
335 auto js = json::parse(inventoryJson);
336
337 if ((js.find("frus") == js.end()) ||
338 (js["frus"].find(file) == js["frus"].end()))
339 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530340 cout << "Device path not in JSON, ignoring" << std::endl;
341 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530342 }
343
344 // Open the file in binary mode
345 ifstream vpdFile(file, ios::binary);
346 // Read the content of the binary file into a vector
347 Binary vpdVector((istreambuf_iterator<char>(vpdFile)),
348 istreambuf_iterator<char>());
349
350 vpdType type = vpdTypeCheck(vpdVector);
351
352 switch (type)
353 {
354 case IPZ_VPD:
355 {
356 // Invoking IPZ Vpd Parser
357 auto vpdStore = parse(move(vpdVector));
358 const Parsed& vpdMap = vpdStore.getVpdMap();
359 string preIntrStr = "com.ibm.ipzvpd.";
360 // Write it to the inventory
361 populateDbus(vpdMap, js, file, preIntrStr);
362 }
363 break;
364
365 case KEYWORD_VPD:
366 {
367 // Creating Keyword Vpd Parser Object
368 KeywordVpdParser parserObj(move(vpdVector));
369 // Invoking KW Vpd Parser
370 const auto& kwValMap = parserObj.parseKwVpd();
371 string preIntrStr = "com.ibm.kwvpd.KWVPD";
372 populateDbus(kwValMap, js, file, preIntrStr);
373 }
374 break;
375 default:
376 throw std::runtime_error("Invalid VPD format");
377 }
378 }
379 catch (exception& e)
380 {
381 cerr << e.what() << "\n";
382 rc = -1;
383 }
384
385 return rc;
386}