blob: 37fede1e67318bbede93442e5fca748a93c7f5f1 [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
25void VpdTool::getPowerSupplyFruPath(vector<string>& powSuppFrus)
26{
27 auto bus = sdbusplus::bus::new_default();
28 auto properties = bus.new_method_call(
29 OBJECT_MAPPER_SERVICE, OBJECT_MAPPER_OBJECT,
30 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths");
31 properties.append(INVENTORY_PATH);
32 properties.append(0);
PriyangaRamasamy0407b172020-03-31 13:57:18 +053033 properties.append(array<const char*, 1>{POWER_SUPPLY_TYPE_INTERFACE});
PriyangaRamasamycdf943c2020-03-18 02:25:30 +053034
35 auto result = bus.call(properties);
36
37 if (result.is_method_error())
38 {
39 throw runtime_error(
40 "GetSubTreePaths api in ObjectMapper service is failed.");
41 }
42
43 result.read(powSuppFrus);
44}
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +053045
46void VpdTool::debugger(json output)
47{
48 cout << output.dump(4) << '\n';
49}
50
51auto VpdTool::makeDBusCall(const string& objectName, const string& interface,
52 const string& kw)
53{
54 auto bus = sdbusplus::bus::new_default();
55 auto properties =
56 bus.new_method_call(INVENTORY_MANAGER_SERVICE, objectName.c_str(),
57 "org.freedesktop.DBus.Properties", "Get");
58 properties.append(interface);
59 properties.append(kw);
60 auto result = bus.call(properties);
61
62 if (result.is_method_error())
63 {
64 throw runtime_error("Get api failed");
65 }
66 return result;
67}
68
69void VpdTool::addFruTypeAndLocation(json exIntf, const string& object,
70 json& kwVal)
71{
PriyangaRamasamycdf943c2020-03-18 02:25:30 +053072 if (object.find("powersupply") != string::npos)
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +053073 {
PriyangaRamasamycdf943c2020-03-18 02:25:30 +053074 kwVal.emplace("type", POWER_SUPPLY_TYPE_INTERFACE);
75 }
76
77 // add else if statement for fan fru
78
79 else
80 {
81 for (const auto& intf : exIntf.items())
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +053082 {
PriyangaRamasamycdf943c2020-03-18 02:25:30 +053083 if ((intf.key().find("Item") != string::npos) &&
84 (intf.value().is_null()))
85 {
86 kwVal.emplace("type", intf.key());
87 break;
88 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +053089 }
90 }
91
92 // Add location code.
93 constexpr auto LOCATION_CODE_IF = "com.ibm.ipzvpd.Location";
94 constexpr auto LOCATION_CODE_PROP = "LocationCode";
95
96 try
97 {
98 variant<string> response;
99 makeDBusCall(object, LOCATION_CODE_IF, LOCATION_CODE_PROP)
100 .read(response);
101
102 if (auto prop = get_if<string>(&response))
103 {
104 kwVal.emplace(LOCATION_CODE_PROP, *prop);
105 }
106 }
107 catch (const SdBusError& e)
108 {
109 kwVal.emplace(LOCATION_CODE_PROP, "");
110 }
111}
112
113json VpdTool::getVINIProperties(string invPath, json exIntf)
114{
115 variant<Binary> response;
116 json output = json::object({});
117 json kwVal = json::object({});
118
119 vector<string> keyword{"CC", "SN", "PN", "FN", "DR"};
120 string interface = "com.ibm.ipzvpd.VINI";
PriyangaRamasamycdf943c2020-03-18 02:25:30 +0530121 string objectName = {};
122
123 if (invPath.find(INVENTORY_PATH) != string::npos)
124 {
125 objectName = invPath;
126 eraseInventoryPath(invPath);
127 }
128 else
129 {
130 objectName = INVENTORY_PATH + invPath;
131 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530132
133 for (string kw : keyword)
134 {
135 try
136 {
137 makeDBusCall(objectName, interface, kw).read(response);
138
139 if (auto vec = get_if<Binary>(&response))
140 {
141 kwVal.emplace(kw, string(vec->begin(), vec->end()));
142 }
143 }
144 catch (const SdBusError& e)
145 {
146 output.emplace(invPath, json::object({}));
147 }
148 }
149
150 addFruTypeAndLocation(exIntf, objectName, kwVal);
151 output.emplace(invPath, kwVal);
152 return output;
153}
154
155void VpdTool::getExtraInterfaceProperties(string invPath, string extraInterface,
156 json prop, json exIntf, json& output)
157{
158 variant<string> response;
159
160 string objectName = INVENTORY_PATH + invPath;
161
162 for (const auto& itProp : prop.items())
163 {
164 string kw = itProp.key();
165 try
166 {
167 makeDBusCall(objectName, extraInterface, kw).read(response);
168
169 if (auto str = get_if<string>(&response))
170 {
171 output.emplace(kw, *str);
172 }
173 }
174 catch (const SdBusError& e)
175 {
176 output.emplace(invPath, json::object({}));
177 }
178 }
179 addFruTypeAndLocation(exIntf, objectName, output);
180}
181
182json VpdTool::interfaceDecider(json& itemEEPROM)
183{
184 if (itemEEPROM.find("inventoryPath") == itemEEPROM.end())
185 {
186 throw runtime_error("Inventory Path not found");
187 }
188
189 if (itemEEPROM.find("extraInterfaces") == itemEEPROM.end())
190 {
191 throw runtime_error("Extra Interfaces not found");
192 }
193
194 bool exIntfCheck = false;
195 json output = json::object({});
196
197 if (itemEEPROM.value("inherit", true))
198 {
199 json j = getVINIProperties(itemEEPROM.at("inventoryPath"),
200 itemEEPROM["extraInterfaces"]);
201 output.insert(j.begin(), j.end());
202 }
203 else
204 {
205 json js;
206 for (const auto& ex : itemEEPROM["extraInterfaces"].items())
207 {
208 if (!(ex.value().is_null()))
209 {
210 exIntfCheck = true;
211 getExtraInterfaceProperties(itemEEPROM.at("inventoryPath"),
212 ex.key(), ex.value(),
213 itemEEPROM["extraInterfaces"], js);
214 }
215 }
216 output.emplace(itemEEPROM.at("inventoryPath"), js);
217 }
218 return output;
219}
220
221json VpdTool::parseInvJson(const json& jsObject, char flag, string fruPath)
222{
223 json output = json::object({});
224 bool validObject = false;
225
226 if (jsObject.find("frus") == jsObject.end())
227 {
228 throw runtime_error("Frus missing in Inventory json");
229 }
230 else
231 {
232 for (const auto& itemFRUS : jsObject["frus"].items())
233 {
234 for (auto itemEEPROM : itemFRUS.value())
235 {
236 try
237 {
238 if (flag == 'O')
239 {
240 if (itemEEPROM.find("inventoryPath") ==
241 itemEEPROM.end())
242 {
243 throw runtime_error("Inventory Path not found");
244 }
245
246 else if (itemEEPROM.at("inventoryPath") == fruPath)
247 {
248 validObject = true;
249 json j = interfaceDecider(itemEEPROM);
250 output.insert(j.begin(), j.end());
251 return output;
252 }
253 }
254 else
255 {
256 json j = interfaceDecider(itemEEPROM);
257 output.insert(j.begin(), j.end());
258 }
259 }
260 catch (exception& e)
261 {
262 cerr << e.what();
263 }
264 }
265 }
266 if ((flag == 'O') && (!validObject))
267 {
268 throw runtime_error(
269 "Invalid object path. Refer --dumpInventory/-i option.");
270 }
271 }
272 return output;
273}
274
275void VpdTool::dumpInventory(const nlohmann::basic_json<>& jsObject)
276{
277 char flag = 'I';
PriyangaRamasamycdf943c2020-03-18 02:25:30 +0530278 json output = json::array({});
279 output.emplace_back(parseInvJson(jsObject, flag, ""));
280
281 vector<string> powSuppFrus;
282
283 getPowerSupplyFruPath(powSuppFrus);
284
285 for (const auto& fru : powSuppFrus)
286 {
287 output.emplace_back(
288 getVINIProperties(fru, nlohmann::detail::value_t::null));
289 }
290
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530291 debugger(output);
292}
293
294void VpdTool::dumpObject(const nlohmann::basic_json<>& jsObject)
295{
296 char flag = 'O';
PriyangaRamasamycdf943c2020-03-18 02:25:30 +0530297 json output = json::array({});
298 vector<string> powSuppFrus;
299
300 getPowerSupplyFruPath(powSuppFrus);
301
302 if (find(powSuppFrus.begin(), powSuppFrus.end(), fruPath) !=
303 powSuppFrus.end())
304 {
305 output.emplace_back(
306 getVINIProperties(fruPath, nlohmann::detail::value_t::null));
307 }
308 else
309 {
310 output.emplace_back(parseInvJson(jsObject, flag, fruPath));
311 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530312 debugger(output);
313}
PriyangaRamasamy02d4d4e2020-02-24 14:54:45 +0530314
315void VpdTool::readKeyword()
316{
317 string interface = "com.ibm.ipzvpd.";
318 variant<Binary> response;
319
320 try
321 {
322 json output = json::object({});
323 json kwVal = json::object({});
324 makeDBusCall(INVENTORY_PATH + fruPath, interface + recordName, keyword)
325 .read(response);
326
327 if (auto vec = get_if<Binary>(&response))
328 {
329 kwVal.emplace(keyword, string(vec->begin(), vec->end()));
330 }
331
332 output.emplace(fruPath, kwVal);
333
334 debugger(output);
335 }
336 catch (json::exception& e)
337 {
338 json output = json::object({});
339 json kwVal = json::object({});
340
341 if (e.id == 316) // invalid UTF-8 byte exception
342 {
343 stringstream ss;
344 string hexByte;
345 string hexRep = "0x";
346 ss << hexRep;
347 hexByte = ss.str();
348
349 // convert Decimal to Hex
350 if (auto resp = get_if<Binary>(&response))
351 {
352 for (auto& vec : *resp)
353 {
354 if ((int)vec == 0)
355 {
356 ss << hex << (int)vec;
357 hexByte = ss.str();
358 }
359 ss << hex << (int)vec;
360 hexByte = ss.str();
361 }
362 }
363
364 kwVal.emplace(keyword, hexByte);
365 output.emplace(fruPath, kwVal);
366
367 debugger(output);
368 }
369 }
370}
PriyangaRamasamyd09d2ec2020-03-12 14:11:50 +0530371
372int VpdTool::updateKeyword()
373{
374 Binary val;
375
376 if (value.find("0x") == string::npos)
377 {
378 val.assign(value.begin(), value.end());
379 }
380 else if (value.find("0x") != string::npos)
381 {
382 stringstream ss;
383 ss.str(value.substr(2));
384 string byteStr{};
385
386 while (!ss.eof())
387 {
388 ss >> setw(2) >> byteStr;
389 uint8_t byte = strtoul(byteStr.c_str(), nullptr, 16);
390
391 val.push_back(byte);
392 }
393 }
394
395 else
396 {
397 throw runtime_error("The value to be updated should be either in ascii "
398 "or in hex. Refer --help option");
399 }
400
401 // writeKeyword(fruPath, recordName, keyword, val);
402
403 auto bus = sdbusplus::bus::new_default();
404 auto properties =
405 bus.new_method_call(BUSNAME, OBJPATH, IFACE, "WriteKeyword");
406 properties.append(static_cast<sdbusplus::message::object_path>(fruPath));
407 properties.append(recordName);
408 properties.append(keyword);
409 properties.append(val);
410 auto result = bus.call(properties);
411
412 if (result.is_method_error())
413 {
414 throw runtime_error("Get api failed");
415 }
416 return 0;
417}
PriyangaRamasamy0407b172020-03-31 13:57:18 +0530418
419void VpdTool::forceReset(const nlohmann::basic_json<>& jsObject)
420{
421 for (const auto& itemFRUS : jsObject["frus"].items())
422 {
423 for (const auto& itemEEPROM : itemFRUS.value().items())
424 {
425 string fru = itemEEPROM.value().at("inventoryPath");
426
427 fs::path fruCachePath = INVENTORY_MANAGER_CACHE;
428 fruCachePath += INVENTORY_PATH;
429 fruCachePath += fru;
430
431 try
432 {
433 for (const auto& it : fs::directory_iterator(fruCachePath))
434 {
435 if (fs::is_regular_file(it.status()))
436 {
437 fs::remove(it);
438 }
439 }
440 }
441 catch (const fs::filesystem_error& e)
442 {
443 }
444 }
445 }
446
447 string udevRemove = "udevadm trigger -c remove -s \"*nvmem*\" -v";
448 system(udevRemove.c_str());
449
450 string invManagerRestart =
451 "systemctl restart xyz.openbmc_project.Inventory.Manager.service";
452 system(invManagerRestart.c_str());
453
454 string sysVpdStop = "systemctl stop system-vpd.service";
455 system(sysVpdStop.c_str());
456
457 string udevAdd = "udevadm trigger -c add -s \"*nvmem*\" -v";
458 system(udevAdd.c_str());
459}