blob: 827a370a4d124cf3dd500c6673533ee2623b88e2 [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>
10#include <exception>
11#include <fstream>
12#include <iostream>
13#include <iterator>
14#include <nlohmann/json.hpp>
15
16using namespace std;
17using namespace openpower::vpd;
18using namespace CLI;
19using namespace vpd::keyword::parser;
20using namespace vpdFormat;
21
22/** @brief Encodes a keyword for D-Bus.
23 */
24static string encodeKeyword(const string& kw, const string& encoding)
25{
26 if (encoding == "MAC")
27 {
28 string res{};
29 size_t first = kw[0];
30 res += toHex(first >> 4);
31 res += toHex(first & 0x0f);
32 for (size_t i = 1; i < kw.size(); ++i)
33 {
34 res += ":";
35 res += toHex(kw[i] >> 4);
36 res += toHex(kw[i] & 0x0f);
37 }
38 return res;
39 }
Alpana Kumari31970de2020-02-17 06:49:57 -060040 else if (encoding == "DATE")
41 {
42 // Date, represent as
43 // <year>-<month>-<day> <hour>:<min>
44 string res{};
45 static constexpr uint8_t skipPrefix = 3;
46
47 auto strItr = kw.begin();
48 advance(strItr, skipPrefix);
49 for_each(strItr, kw.end(), [&res](size_t c) { res += c; });
50
51 res.insert(BD_YEAR_END, 1, '-');
52 res.insert(BD_MONTH_END, 1, '-');
53 res.insert(BD_DAY_END, 1, ' ');
54 res.insert(BD_HOUR_END, 1, ':');
55
56 return res;
57 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +053058 else // default to string encoding
59 {
60 return string(kw.begin(), kw.end());
61 }
62}
63
64/**
65 * @brief Populate FRU specific interfaces.
66 *
67 * This is a common method which handles both
68 * ipz and keyword specific interfaces thus,
69 * reducing the code redundancy.
70 * @param[in] map - Reference to the innermost keyword-value map.
71 * @param[in] preIntrStr - Reference to the interface string.
72 * @param[out] interfaces - Reference to interface map.
73 */
74template <typename T>
75static void populateFruSpecificInterfaces(const T& map,
76 const string& preIntrStr,
77 inventory::InterfaceMap& interfaces)
78{
79 inventory::PropertyMap prop;
80
81 for (const auto& kwVal : map)
82 {
83 std::vector<uint8_t> vec(kwVal.second.begin(), kwVal.second.end());
84
85 auto kw = kwVal.first;
86
87 if (kw[0] == '#')
88 {
89 kw = std::string("PD_") + kw[1];
90 }
91 prop.emplace(move(kw), move(vec));
92 }
93
94 interfaces.emplace(preIntrStr, move(prop));
95}
96
97/**
98 * @brief Populate Interfaces.
99 *
100 * This method populates common and extra interfaces to dbus.
101 * @param[in] js - json object
102 * @param[out] interfaces - Reference to interface map
103 * @param[in] vpdMap - Reference to the parsed vpd map.
104 */
105template <typename T>
106static void populateInterfaces(const nlohmann::json& js,
107 inventory::InterfaceMap& interfaces,
108 const T& vpdMap)
109{
110 for (const auto& ifs : js.items())
111 {
112 const string& inf = ifs.key();
113 inventory::PropertyMap props;
114
115 for (const auto& itr : ifs.value().items())
116 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600117 // check if the Value is boolean or object
118 if (itr.value().is_boolean())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530119 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600120 props.emplace(itr.key(), itr.value().get<bool>());
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530121 }
Alpana Kumari31970de2020-02-17 06:49:57 -0600122 else if (itr.value().is_object())
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530123 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600124 const string& rec = itr.value().value("recordName", "");
125 const string& kw = itr.value().value("keywordName", "");
126 const string& encoding = itr.value().value("encoding", "");
127
128 if constexpr (std::is_same<T, Parsed>::value)
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530129 {
Alpana Kumari31970de2020-02-17 06:49:57 -0600130 if (!rec.empty() && !kw.empty() &&
131 vpdMap.at(rec).count(kw) && vpdMap.count(rec))
132 {
133 auto encoded =
134 encodeKeyword(vpdMap.at(rec).at(kw), encoding);
135 props.emplace(itr.key(), encoded);
136 }
137 }
138 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
139 {
140 if (!kw.empty() && vpdMap.count(kw))
141 {
142 auto prop =
143 string(vpdMap.at(kw).begin(), vpdMap.at(kw).end());
144 auto encoded = encodeKeyword(prop, encoding);
145 props.emplace(itr.key(), encoded);
146 }
PriyangaRamasamyabb87ed2019-11-19 17:25:35 +0530147 }
148 }
149 }
150 interfaces.emplace(inf, move(props));
151 }
152}
153
154/**
155 * @brief Populate Dbus.
156 *
157 * This method invokes all the populateInterface functions
158 * and notifies PIM about dbus object.
159 * @param[in] vpdMap - Either IPZ vpd map or Keyword vpd map based on the input.
160 * @param[in] js - Inventory json object
161 * @param[in] filePath - Path of the vpd file
162 * @param[in] preIntrStr - Interface string
163 */
164template <typename T>
165static void populateDbus(const T& vpdMap, nlohmann::json& js,
166 const string& filePath, const string& preIntrStr)
167{
168 inventory::InterfaceMap interfaces;
169 inventory::ObjectMap objects;
170 inventory::PropertyMap prop;
171
172 for (const auto& item : js["frus"][filePath])
173 {
174 const auto& objectPath = item["inventoryPath"];
175 sdbusplus::message::object_path object(objectPath);
176 // Populate the VPD keywords and the common interfaces only if we
177 // are asked to inherit that data from the VPD, else only add the
178 // extraInterfaces.
179 if (item.value("inherit", true))
180 {
181 if constexpr (std::is_same<T, Parsed>::value)
182 {
183 // Each record in the VPD becomes an interface and all keyword
184 // within the record are properties under that interface.
185 for (const auto& record : vpdMap)
186 {
187 populateFruSpecificInterfaces(
188 record.second, preIntrStr + record.first, interfaces);
189 }
190 }
191 else if constexpr (std::is_same<T, KeywordVpdMap>::value)
192 {
193 populateFruSpecificInterfaces(vpdMap, preIntrStr, interfaces);
194 }
195 }
196
197 // Populate interfaces and properties that are common to every FRU
198 // and additional interface that might be defined on a per-FRU basis.
199
200 if (item.find("commonInterfaces") != item.end())
201 {
202 populateInterfaces(item["commonInterfaces"], interfaces, vpdMap);
203 }
204 if (item.find("extraInterfaces") != item.end())
205 {
206 populateInterfaces(item["extraInterfaces"], interfaces, vpdMap);
207 }
208 objects.emplace(move(object), move(interfaces));
209 }
210
211 // Notify PIM
212 inventory::callPIM(move(objects));
213}
214
215int main(int argc, char** argv)
216{
217 int rc = 0;
218
219 try
220 {
221 using json = nlohmann::json;
222
223 App app{"ibm-read-vpd - App to read IPZ format VPD, parse it and store "
224 "in DBUS"};
225 string file{};
226
227 app.add_option("-f, --file", file, "File containing VPD (IPZ/KEYWORD)")
228 ->required()
229 ->check(ExistingFile);
230
231 CLI11_PARSE(app, argc, argv);
232
233 // Make sure that the file path we get is for a supported EEPROM
234 ifstream inventoryJson(INVENTORY_JSON);
235 auto js = json::parse(inventoryJson);
236
237 if ((js.find("frus") == js.end()) ||
238 (js["frus"].find(file) == js["frus"].end()))
239 {
240 throw std::runtime_error("Device path missing in inventory JSON");
241 }
242
243 // Open the file in binary mode
244 ifstream vpdFile(file, ios::binary);
245 // Read the content of the binary file into a vector
246 Binary vpdVector((istreambuf_iterator<char>(vpdFile)),
247 istreambuf_iterator<char>());
248
249 vpdType type = vpdTypeCheck(vpdVector);
250
251 switch (type)
252 {
253 case IPZ_VPD:
254 {
255 // Invoking IPZ Vpd Parser
256 auto vpdStore = parse(move(vpdVector));
257 const Parsed& vpdMap = vpdStore.getVpdMap();
258 string preIntrStr = "com.ibm.ipzvpd.";
259 // Write it to the inventory
260 populateDbus(vpdMap, js, file, preIntrStr);
261 }
262 break;
263
264 case KEYWORD_VPD:
265 {
266 // Creating Keyword Vpd Parser Object
267 KeywordVpdParser parserObj(move(vpdVector));
268 // Invoking KW Vpd Parser
269 const auto& kwValMap = parserObj.parseKwVpd();
270 string preIntrStr = "com.ibm.kwvpd.KWVPD";
271 populateDbus(kwValMap, js, file, preIntrStr);
272 }
273 break;
274 default:
275 throw std::runtime_error("Invalid VPD format");
276 }
277 }
278 catch (exception& e)
279 {
280 cerr << e.what() << "\n";
281 rc = -1;
282 }
283
284 return rc;
285}