blob: ddbf7e928aa0ae2c2cf92459e33585d3b17c9425 [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
23/** @brief Encodes a keyword for D-Bus.
24 */
25static string encodeKeyword(const string& kw, const string& encoding)
26{
27 if (encoding == "MAC")
28 {
29 string res{};
30 size_t first = kw[0];
31 res += toHex(first >> 4);
32 res += toHex(first & 0x0f);
33 for (size_t i = 1; i < kw.size(); ++i)
34 {
35 res += ":";
36 res += toHex(kw[i] >> 4);
37 res += toHex(kw[i] & 0x0f);
38 }
39 return res;
40 }
Alpana Kumari31970de2020-02-17 06:49:57 -060041 else if (encoding == "DATE")
42 {
43 // Date, represent as
44 // <year>-<month>-<day> <hour>:<min>
45 string res{};
46 static constexpr uint8_t skipPrefix = 3;
47
48 auto strItr = kw.begin();
49 advance(strItr, skipPrefix);
50 for_each(strItr, kw.end(), [&res](size_t c) { res += c; });
51
52 res.insert(BD_YEAR_END, 1, '-');
53 res.insert(BD_MONTH_END, 1, '-');
54 res.insert(BD_DAY_END, 1, ' ');
55 res.insert(BD_HOUR_END, 1, ':');
56
57 return res;
58 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053059 else // default to string encoding
60 {
61 return string(kw.begin(), kw.end());
62 }
63}
64
Santosh Puranik88edeb62020-03-02 12:00:09 +053065/** @brief Reads a property from the inventory manager given object path,
66 * intreface and property.
67 */
68static auto readBusProperty(const string& obj, const string& inf,
69 const string& prop)
70{
71 string propVal{};
72 static constexpr auto OBJ_PREFIX = "/xyz/openbmc_project/inventory";
73 string object = OBJ_PREFIX + obj;
74 auto bus = sdbusplus::bus::new_default();
75 auto properties = bus.new_method_call(
76 "xyz.openbmc_project.Inventory.Manager", object.c_str(),
77 "org.freedesktop.DBus.Properties", "Get");
78 properties.append(inf);
79 properties.append(prop);
80 auto result = bus.call(properties);
81 if (!result.is_method_error())
82 {
83 variant<Binary> val;
84 result.read(val);
85
86 if (auto pVal = get_if<Binary>(&val))
87 {
88 propVal.assign(reinterpret_cast<const char*>(pVal->data()),
89 pVal->size());
90 }
91 }
92 return propVal;
93}
94
95/**
96 * @brief Expands location codes
97 */
98static auto expandLocationCode(const string& unexpanded, const Parsed& vpdMap,
99 bool isSystemVpd)
100{
101 auto expanded{unexpanded};
102 static constexpr auto SYSTEM_OBJECT = "/system/chassis/motherboard";
103 static constexpr auto VCEN_IF = "com.ibm.ipzvpd.VCEN";
104 static constexpr auto VSYS_IF = "com.ibm.ipzvpd.VSYS";
105 size_t idx = expanded.find("fcs");
106 try
107 {
108 if (idx != string::npos)
109 {
110 string fc{};
111 string se{};
112 if (isSystemVpd)
113 {
114 const auto& fcData = vpdMap.at("VCEN").at("FC");
115 const auto& seData = vpdMap.at("VCEN").at("SE");
116 fc = string(fcData.data(), fcData.size());
117 se = string(seData.data(), seData.size());
118 }
119 else
120 {
121 fc = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "FC");
122 se = readBusProperty(SYSTEM_OBJECT, VCEN_IF, "SE");
123 }
124
125 // TODO: See if ND1 can be placed in the JSON
126 expanded.replace(idx, 3, fc.substr(0, 4) + ".ND1." + se);
127 }
128 else
129 {
130 idx = expanded.find("mts");
131 if (idx != string::npos)
132 {
133 string mt{};
134 string se{};
135 if (isSystemVpd)
136 {
137 const auto& mtData = vpdMap.at("VSYS").at("TM");
138 const auto& seData = vpdMap.at("VSYS").at("SE");
139 mt = string(mtData.data(), mtData.size());
140 se = string(seData.data(), seData.size());
141 }
142 else
143 {
144 mt = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "TM");
145 se = readBusProperty(SYSTEM_OBJECT, VSYS_IF, "SE");
146 }
147
148 replace(mt.begin(), mt.end(), '-', '.');
149 expanded.replace(idx, 3, mt + "." + se);
150 }
151 }
152 }
153 catch (std::exception& e)
154 {
155 std::cerr << "Failed to expand location code with exception: "
156 << e.what() << "\n";
157 }
158 return expanded;
159}
160
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530161/**
162 * @brief Populate FRU specific interfaces.
163 *
164 * This is a common method which handles both
165 * ipz and keyword specific interfaces thus,
166 * reducing the code redundancy.
167 * @param[in] map - Reference to the innermost keyword-value map.
168 * @param[in] preIntrStr - Reference to the interface string.
169 * @param[out] interfaces - Reference to interface map.
170 */
171template <typename T>
172static void populateFruSpecificInterfaces(const T& map,
173 const string& preIntrStr,
174 inventory::InterfaceMap& interfaces)
175{
176 inventory::PropertyMap prop;
177
178 for (const auto& kwVal : map)
179 {
180 std::vector<uint8_t> vec(kwVal.second.begin(), kwVal.second.end());
181
182 auto kw = kwVal.first;
183
184 if (kw[0] == '#')
185 {
186 kw = std::string("PD_") + kw[1];
187 }
188 prop.emplace(move(kw), move(vec));
189 }
190
191 interfaces.emplace(preIntrStr, move(prop));
192}
193
194/**
195 * @brief Populate Interfaces.
196 *
197 * This method populates common and extra interfaces to dbus.
198 * @param[in] js - json object
199 * @param[out] interfaces - Reference to interface map
200 * @param[in] vpdMap - Reference to the parsed vpd map.
Santosh Puranik88edeb62020-03-02 12:00:09 +0530201 * @param[in] isSystemVpd - Denotes whether we are collecting the system VPD.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530202 */
203template <typename T>
204static void populateInterfaces(const nlohmann::json& js,
205 inventory::InterfaceMap& interfaces,
Santosh Puranik88edeb62020-03-02 12:00:09 +0530206 const T& vpdMap, bool isSystemVpd)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530207{
208 for (const auto& ifs : js.items())
209 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530210 string inf = ifs.key();
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530211 inventory::PropertyMap props;
212
213 for (const auto& itr : ifs.value().items())
214 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530215 const string& busProp = itr.key();
216
Alpana Kumari31970de2020-02-17 06:49:57 -0600217 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530218 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530219 props.emplace(busProp, itr.value().get<bool>());
220 }
221 else if (itr.value().is_string())
222 {
223 if constexpr (std::is_same<T, Parsed>::value)
224 {
225 if (busProp == "LocationCode" &&
226 inf == "com.ibm.ipzvpd.Location")
227 {
228 auto prop = expandLocationCode(
229 itr.value().get<string>(), vpdMap, isSystemVpd);
230 props.emplace(busProp, prop);
231 }
232 else
233 {
234 props.emplace(busProp, itr.value().get<string>());
235 }
236 }
237 else
238 {
239 props.emplace(busProp, itr.value().get<string>());
240 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530241 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600242 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530243 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600244 const string& rec = itr.value().value("recordName", "");
245 const string& kw = itr.value().value("keywordName", "");
246 const string& encoding = itr.value().value("encoding", "");
247
248 if constexpr (std::is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530249 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530250 if (!rec.empty() && !kw.empty() && vpdMap.count(rec) &&
251 vpdMap.at(rec).count(kw))
Alpana Kumari31970de2020-02-17 06:49:57 -0600252 {
253 auto encoded =
254 encodeKeyword(vpdMap.at(rec).at(kw), encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530255 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600256 }
257 }
258 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
259 {
260 if (!kw.empty() && vpdMap.count(kw))
261 {
262 auto prop =
263 string(vpdMap.at(kw).begin(), vpdMap.at(kw).end());
264 auto encoded = encodeKeyword(prop, encoding);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530265 props.emplace(busProp, encoded);
Alpana Kumari31970de2020-02-17 06:49:57 -0600266 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530267 }
268 }
269 }
270 interfaces.emplace(inf, move(props));
271 }
272}
273
274/**
275 * @brief Populate Dbus.
276 *
277 * This method invokes all the populateInterface functions
278 * and notifies PIM about dbus object.
279 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the input.
280 * @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
292 for (const auto& item : js["frus"][filePath])
293 {
294 const auto& objectPath = item["inventoryPath"];
295 sdbusplus::message::object_path object(objectPath);
Santosh Puranik88edeb62020-03-02 12:00:09 +0530296 auto 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 {
304 // Each record in the VPD becomes an interface and all keyword
305 // within the record are properties under that interface.
306 for (const auto& record : vpdMap)
307 {
308 populateFruSpecificInterfaces(
309 record.second, preIntrStr + record.first, interfaces);
310 }
311 }
312 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
313 {
314 populateFruSpecificInterfaces(vpdMap, preIntrStr, interfaces);
315 }
Santosh Puranik88edeb62020-03-02 12:00:09 +0530316 if (js.find("commonInterfaces") != js.end())
317 {
318 populateInterfaces(js["commonInterfaces"], interfaces, vpdMap,
319 isSystemVpd);
320 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530321 }
322
323 // Populate interfaces and properties that are common to every FRU
324 // and additional interface that might be defined on a per-FRU basis.
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530325 if (item.find("extraInterfaces") != item.end())
326 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530327 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap,
328 isSystemVpd);
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530329 }
330 objects.emplace(move(object), move(interfaces));
331 }
332
333 // Notify PIM
334 inventory::callPIM(move(objects));
335}
336
337int main(int argc, char** argv)
338{
339 int rc = 0;
340
341 try
342 {
343 using json = nlohmann::json;
344
345 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
346 "in DBUS"};
347 string file{};
348
349 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
350 ->required()
351 ->check(ExistingFile);
352
353 CLI11_PARSE(app, argc, argv);
354
355 // Make sure that the file path we get is for a supported EEPROM
356 ifstream inventoryJson(INVENTORY_JSON);
357 auto js = json::parse(inventoryJson);
358
359 if ((js.find("frus") == js.end()) ||
360 (js["frus"].find(file) == js["frus"].end()))
361 {
Santosh Puranik88edeb62020-03-02 12:00:09 +0530362 cout << "Device path not in JSON, ignoring" << std::endl;
363 return 0;
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530364 }
365
366 // Open the file in binary mode
367 ifstream vpdFile(file, ios::binary);
368 // Read the content of the binary file into a vector
369 Binary vpdVector((istreambuf_iterator<char>(vpdFile)),
370 istreambuf_iterator<char>());
371
372 vpdType type = vpdTypeCheck(vpdVector);
373
374 switch (type)
375 {
376 case IPZ_VPD:
377 {
378 // Invoking IPZ Vpd Parser
379 auto vpdStore = parse(move(vpdVector));
380 const Parsed& vpdMap = vpdStore.getVpdMap();
381 string preIntrStr = "com.ibm.ipzvpd.";
382 // Write it to the inventory
383 populateDbus(vpdMap, js, file, preIntrStr);
384 }
385 break;
386
387 case KEYWORD_VPD:
388 {
389 // Creating Keyword Vpd Parser Object
390 KeywordVpdParser parserObj(move(vpdVector));
391 // Invoking KW Vpd Parser
392 const auto& kwValMap = parserObj.parseKwVpd();
393 string preIntrStr = "com.ibm.kwvpd.KWVPD";
394 populateDbus(kwValMap, js, file, preIntrStr);
395 }
396 break;
397 default:
398 throw std::runtime_error("Invalid VPD format");
399 }
400 }
401 catch (exception& e)
402 {
403 cerr << e.what() << "\n";
404 rc = -1;
405 }
406
407 return rc;
408}