blob: c24eb93ca52a0debdfd204e82a0f18f4d12f0a16 [file] [log] [blame]
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +05301#include "vpd_tool_impl.hpp"
2
PriyangaRamasamycdf943c2020-03-18 02:25:30 +05303#include <cstdlib>
4#include <filesystem>
PriyangaRamasamyd09d2ec2020-03-12 14:11:50 +05305#include <iomanip>
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +05306#include <iostream>
7#include <sdbusplus/bus.hpp>
8#include <sstream>
9#include <variant>
10#include <vector>
11
12using namespace std;
13using sdbusplus::exception::SdBusError;
14using namespace openpower::vpd;
PriyangaRamasamycdf943c2020-03-18 02:25:30 +053015namespace fs = std::filesystem;
16
17void VpdTool::eraseInventoryPath(string& fru)
18{
19 // Power supply frupath comes with INVENTORY_PATH appended in prefix.
20 // Stripping it off inorder to avoid INVENTORY_PATH duplication
21 // during getVINIProperties() execution.
22 fru.erase(0, sizeof(INVENTORY_PATH) - 1);
23}
24
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +053025void VpdTool::debugger(json output)
26{
27 cout << output.dump(4) << '\n';
28}
29
30auto VpdTool::makeDBusCall(const string& objectName, const string& interface,
31 const string& kw)
32{
33 auto bus = sdbusplus::bus::new_default();
34 auto properties =
35 bus.new_method_call(INVENTORY_MANAGER_SERVICE, objectName.c_str(),
36 "org.freedesktop.DBus.Properties", "Get");
37 properties.append(interface);
38 properties.append(kw);
39 auto result = bus.call(properties);
40
41 if (result.is_method_error())
42 {
43 throw runtime_error("Get api failed");
44 }
45 return result;
46}
47
48void VpdTool::addFruTypeAndLocation(json exIntf, const string& object,
49 json& kwVal)
50{
PriyangaRamasamycdf943c2020-03-18 02:25:30 +053051 if (object.find("powersupply") != string::npos)
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +053052 {
PriyangaRamasamycdf943c2020-03-18 02:25:30 +053053 kwVal.emplace("type", POWER_SUPPLY_TYPE_INTERFACE);
54 }
55
56 // add else if statement for fan fru
57
58 else
59 {
60 for (const auto& intf : exIntf.items())
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +053061 {
PriyangaRamasamycdf943c2020-03-18 02:25:30 +053062 if ((intf.key().find("Item") != string::npos) &&
63 (intf.value().is_null()))
64 {
65 kwVal.emplace("type", intf.key());
66 break;
67 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +053068 }
69 }
70
71 // Add location code.
72 constexpr auto LOCATION_CODE_IF = "com.ibm.ipzvpd.Location";
73 constexpr auto LOCATION_CODE_PROP = "LocationCode";
74
75 try
76 {
77 variant<string> response;
78 makeDBusCall(object, LOCATION_CODE_IF, LOCATION_CODE_PROP)
79 .read(response);
80
81 if (auto prop = get_if<string>(&response))
82 {
83 kwVal.emplace(LOCATION_CODE_PROP, *prop);
84 }
85 }
86 catch (const SdBusError& e)
87 {
88 kwVal.emplace(LOCATION_CODE_PROP, "");
89 }
90}
91
92json VpdTool::getVINIProperties(string invPath, json exIntf)
93{
94 variant<Binary> response;
95 json output = json::object({});
96 json kwVal = json::object({});
97
98 vector<string> keyword{"CC", "SN", "PN", "FN", "DR"};
99 string interface = "com.ibm.ipzvpd.VINI";
PriyangaRamasamycdf943c2020-03-18 02:25:30 +0530100 string objectName = {};
101
102 if (invPath.find(INVENTORY_PATH) != string::npos)
103 {
104 objectName = invPath;
105 eraseInventoryPath(invPath);
106 }
107 else
108 {
109 objectName = INVENTORY_PATH + invPath;
110 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530111
112 for (string kw : keyword)
113 {
114 try
115 {
116 makeDBusCall(objectName, interface, kw).read(response);
117
118 if (auto vec = get_if<Binary>(&response))
119 {
120 kwVal.emplace(kw, string(vec->begin(), vec->end()));
121 }
122 }
123 catch (const SdBusError& e)
124 {
125 output.emplace(invPath, json::object({}));
126 }
127 }
128
129 addFruTypeAndLocation(exIntf, objectName, kwVal);
Alpana Kumarib6965f12020-06-01 00:32:21 -0500130 kwVal.emplace("TYPE", fruType);
131
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530132 output.emplace(invPath, kwVal);
133 return output;
134}
135
136void VpdTool::getExtraInterfaceProperties(string invPath, string extraInterface,
137 json prop, json exIntf, json& output)
138{
139 variant<string> response;
140
141 string objectName = INVENTORY_PATH + invPath;
142
143 for (const auto& itProp : prop.items())
144 {
145 string kw = itProp.key();
146 try
147 {
148 makeDBusCall(objectName, extraInterface, kw).read(response);
149
150 if (auto str = get_if<string>(&response))
151 {
152 output.emplace(kw, *str);
153 }
154 }
155 catch (const SdBusError& e)
156 {
157 output.emplace(invPath, json::object({}));
158 }
159 }
160 addFruTypeAndLocation(exIntf, objectName, output);
161}
162
163json VpdTool::interfaceDecider(json& itemEEPROM)
164{
165 if (itemEEPROM.find("inventoryPath") == itemEEPROM.end())
166 {
167 throw runtime_error("Inventory Path not found");
168 }
169
170 if (itemEEPROM.find("extraInterfaces") == itemEEPROM.end())
171 {
172 throw runtime_error("Extra Interfaces not found");
173 }
174
175 bool exIntfCheck = false;
176 json output = json::object({});
Alpana Kumarib6965f12020-06-01 00:32:21 -0500177 fruType = "FRU";
178
179 // check type and add FRU Type in object
180 if (itemEEPROM.find("type") != itemEEPROM.end())
181 {
182 fruType = itemEEPROM.at("type");
183 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530184
185 if (itemEEPROM.value("inherit", true))
186 {
187 json j = getVINIProperties(itemEEPROM.at("inventoryPath"),
188 itemEEPROM["extraInterfaces"]);
189 output.insert(j.begin(), j.end());
190 }
191 else
192 {
193 json js;
194 for (const auto& ex : itemEEPROM["extraInterfaces"].items())
195 {
196 if (!(ex.value().is_null()))
197 {
198 exIntfCheck = true;
199 getExtraInterfaceProperties(itemEEPROM.at("inventoryPath"),
200 ex.key(), ex.value(),
201 itemEEPROM["extraInterfaces"], js);
202 }
203 }
204 output.emplace(itemEEPROM.at("inventoryPath"), js);
205 }
Alpana Kumarib6965f12020-06-01 00:32:21 -0500206
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530207 return output;
208}
209
210json VpdTool::parseInvJson(const json& jsObject, char flag, string fruPath)
211{
212 json output = json::object({});
213 bool validObject = false;
214
215 if (jsObject.find("frus") == jsObject.end())
216 {
217 throw runtime_error("Frus missing in Inventory json");
218 }
219 else
220 {
221 for (const auto& itemFRUS : jsObject["frus"].items())
222 {
223 for (auto itemEEPROM : itemFRUS.value())
224 {
225 try
226 {
227 if (flag == 'O')
228 {
229 if (itemEEPROM.find("inventoryPath") ==
230 itemEEPROM.end())
231 {
232 throw runtime_error("Inventory Path not found");
233 }
234
235 else if (itemEEPROM.at("inventoryPath") == fruPath)
236 {
237 validObject = true;
238 json j = interfaceDecider(itemEEPROM);
239 output.insert(j.begin(), j.end());
240 return output;
241 }
242 }
243 else
244 {
245 json j = interfaceDecider(itemEEPROM);
246 output.insert(j.begin(), j.end());
247 }
248 }
249 catch (exception& e)
250 {
251 cerr << e.what();
252 }
253 }
254 }
255 if ((flag == 'O') && (!validObject))
256 {
257 throw runtime_error(
258 "Invalid object path. Refer --dumpInventory/-i option.");
259 }
260 }
261 return output;
262}
263
264void VpdTool::dumpInventory(const nlohmann::basic_json<>& jsObject)
265{
266 char flag = 'I';
PriyangaRamasamycdf943c2020-03-18 02:25:30 +0530267 json output = json::array({});
268 output.emplace_back(parseInvJson(jsObject, flag, ""));
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530269 debugger(output);
270}
271
272void VpdTool::dumpObject(const nlohmann::basic_json<>& jsObject)
273{
274 char flag = 'O';
PriyangaRamasamycdf943c2020-03-18 02:25:30 +0530275 json output = json::array({});
Santosh Puranikd5d52bb2020-07-28 15:08:10 +0530276 output.emplace_back(parseInvJson(jsObject, flag, fruPath));
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530277 debugger(output);
278}
PriyangaRamasamy02d4d4e2020-02-24 14:54:45 +0530279
280void VpdTool::readKeyword()
281{
282 string interface = "com.ibm.ipzvpd.";
283 variant<Binary> response;
284
285 try
286 {
287 json output = json::object({});
288 json kwVal = json::object({});
289 makeDBusCall(INVENTORY_PATH + fruPath, interface + recordName, keyword)
290 .read(response);
291
292 if (auto vec = get_if<Binary>(&response))
293 {
294 kwVal.emplace(keyword, string(vec->begin(), vec->end()));
295 }
296
297 output.emplace(fruPath, kwVal);
298
299 debugger(output);
300 }
301 catch (json::exception& e)
302 {
303 json output = json::object({});
304 json kwVal = json::object({});
305
306 if (e.id == 316) // invalid UTF-8 byte exception
307 {
308 stringstream ss;
309 string hexByte;
310 string hexRep = "0x";
311 ss << hexRep;
312 hexByte = ss.str();
313
314 // convert Decimal to Hex
315 if (auto resp = get_if<Binary>(&response))
316 {
317 for (auto& vec : *resp)
318 {
319 if ((int)vec == 0)
320 {
321 ss << hex << (int)vec;
322 hexByte = ss.str();
323 }
324 ss << hex << (int)vec;
325 hexByte = ss.str();
326 }
327 }
328
329 kwVal.emplace(keyword, hexByte);
330 output.emplace(fruPath, kwVal);
331
332 debugger(output);
333 }
334 }
335}
PriyangaRamasamyd09d2ec2020-03-12 14:11:50 +0530336
337int VpdTool::updateKeyword()
338{
339 Binary val;
340
341 if (value.find("0x") == string::npos)
342 {
343 val.assign(value.begin(), value.end());
344 }
345 else if (value.find("0x") != string::npos)
346 {
347 stringstream ss;
348 ss.str(value.substr(2));
349 string byteStr{};
350
351 while (!ss.eof())
352 {
353 ss >> setw(2) >> byteStr;
354 uint8_t byte = strtoul(byteStr.c_str(), nullptr, 16);
355
356 val.push_back(byte);
357 }
358 }
359
360 else
361 {
362 throw runtime_error("The value to be updated should be either in ascii "
363 "or in hex. Refer --help option");
364 }
365
366 // writeKeyword(fruPath, recordName, keyword, val);
367
368 auto bus = sdbusplus::bus::new_default();
369 auto properties =
370 bus.new_method_call(BUSNAME, OBJPATH, IFACE, "WriteKeyword");
371 properties.append(static_cast<sdbusplus::message::object_path>(fruPath));
372 properties.append(recordName);
373 properties.append(keyword);
374 properties.append(val);
375 auto result = bus.call(properties);
376
377 if (result.is_method_error())
378 {
379 throw runtime_error("Get api failed");
380 }
381 return 0;
382}
PriyangaRamasamy0407b172020-03-31 13:57:18 +0530383
384void VpdTool::forceReset(const nlohmann::basic_json<>& jsObject)
385{
386 for (const auto& itemFRUS : jsObject["frus"].items())
387 {
388 for (const auto& itemEEPROM : itemFRUS.value().items())
389 {
390 string fru = itemEEPROM.value().at("inventoryPath");
391
392 fs::path fruCachePath = INVENTORY_MANAGER_CACHE;
393 fruCachePath += INVENTORY_PATH;
394 fruCachePath += fru;
395
396 try
397 {
398 for (const auto& it : fs::directory_iterator(fruCachePath))
399 {
400 if (fs::is_regular_file(it.status()))
401 {
402 fs::remove(it);
403 }
404 }
405 }
406 catch (const fs::filesystem_error& e)
407 {
408 }
409 }
410 }
411
412 string udevRemove = "udevadm trigger -c remove -s \"*nvmem*\" -v";
413 system(udevRemove.c_str());
414
415 string invManagerRestart =
416 "systemctl restart xyz.openbmc_project.Inventory.Manager.service";
417 system(invManagerRestart.c_str());
418
419 string sysVpdStop = "systemctl stop system-vpd.service";
420 system(sysVpdStop.c_str());
421
422 string udevAdd = "udevadm trigger -c add -s \"*nvmem*\" -v";
423 system(udevAdd.c_str());
424}