blob: 2d777e96ffc3057e8f4d5942a88157eedd636e77 [file] [log] [blame]
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +05301#include "vpd_tool_impl.hpp"
2
Priyanga Ramasamy38031312021-10-07 16:39:13 -05003#include "impl.hpp"
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +05304#include "vpd_exceptions.hpp"
5
PriyangaRamasamycdf943c2020-03-18 02:25:30 +05306#include <cstdlib>
7#include <filesystem>
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +05308#include <iostream>
9#include <sdbusplus/bus.hpp>
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +053010#include <variant>
11#include <vector>
12
13using namespace std;
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +053014using namespace openpower::vpd;
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +053015using namespace inventory;
16using namespace openpower::vpd::manager::editor;
PriyangaRamasamycdf943c2020-03-18 02:25:30 +053017namespace fs = std::filesystem;
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +053018using json = nlohmann::json;
19using namespace openpower::vpd::exceptions;
Priyanga Ramasamy38031312021-10-07 16:39:13 -050020using namespace openpower::vpd::parser;
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +053021
22Binary VpdTool::toBinary(const std::string& value)
23{
24 Binary val{};
25 if (value.find("0x") == string::npos)
26 {
27 val.assign(value.begin(), value.end());
28 }
29 else if (value.find("0x") != string::npos)
30 {
31 stringstream ss;
32 ss.str(value.substr(2));
33 string byteStr{};
34
Priyanga Ramasamyec912e62021-12-15 22:47:51 -060035 if (value.length() % 2 != 0)
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +053036 {
Priyanga Ramasamyec912e62021-12-15 22:47:51 -060037 throw runtime_error(
38 "VPD-TOOL write option accepts 2 digit hex numbers. (Eg. 0x1 "
39 "should be given as 0x01). Aborting the write operation.");
40 }
41
42 if (value.find_first_not_of("0123456789abcdefABCDEF", 2) !=
43 std::string::npos)
44 {
45 throw runtime_error("Provide a valid hexadecimal input.");
46 }
47
48 while (ss >> setw(2) >> byteStr)
49 {
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +053050 uint8_t byte = strtoul(byteStr.c_str(), nullptr, 16);
51
52 val.push_back(byte);
53 }
54 }
55
56 else
57 {
58 throw runtime_error("The value to be updated should be either in ascii "
59 "or in hex. Refer --help option");
60 }
61 return val;
62}
PriyangaRamasamycdf943c2020-03-18 02:25:30 +053063
PriyangaRamasamy6ee637a2021-02-12 04:49:02 -060064void VpdTool::printReturnCode(int returnCode)
65{
66 if (returnCode)
67 {
68 cout << "\n Command failed with the return code " << returnCode
69 << ". Continuing the execution. " << endl;
70 }
71}
72
PriyangaRamasamycdf943c2020-03-18 02:25:30 +053073void VpdTool::eraseInventoryPath(string& fru)
74{
75 // Power supply frupath comes with INVENTORY_PATH appended in prefix.
76 // Stripping it off inorder to avoid INVENTORY_PATH duplication
77 // during getVINIProperties() execution.
78 fru.erase(0, sizeof(INVENTORY_PATH) - 1);
79}
80
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +053081void VpdTool::debugger(json output)
82{
83 cout << output.dump(4) << '\n';
84}
85
86auto VpdTool::makeDBusCall(const string& objectName, const string& interface,
87 const string& kw)
88{
89 auto bus = sdbusplus::bus::new_default();
90 auto properties =
91 bus.new_method_call(INVENTORY_MANAGER_SERVICE, objectName.c_str(),
92 "org.freedesktop.DBus.Properties", "Get");
93 properties.append(interface);
94 properties.append(kw);
95 auto result = bus.call(properties);
96
97 if (result.is_method_error())
98 {
99 throw runtime_error("Get api failed");
100 }
101 return result;
102}
103
PriyangaRamasamy8cc5b152021-06-03 13:05:17 -0500104json VpdTool::getVINIProperties(string invPath)
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530105{
106 variant<Binary> response;
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530107 json kwVal = json::object({});
108
109 vector<string> keyword{"CC", "SN", "PN", "FN", "DR"};
110 string interface = "com.ibm.ipzvpd.VINI";
PriyangaRamasamycdf943c2020-03-18 02:25:30 +0530111 string objectName = {};
112
113 if (invPath.find(INVENTORY_PATH) != string::npos)
114 {
115 objectName = invPath;
116 eraseInventoryPath(invPath);
117 }
118 else
119 {
120 objectName = INVENTORY_PATH + invPath;
121 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530122 for (string kw : keyword)
123 {
124 try
125 {
126 makeDBusCall(objectName, interface, kw).read(response);
127
128 if (auto vec = get_if<Binary>(&response))
129 {
PriyangaRamasamy887a42a2020-09-03 00:33:57 +0530130 string printableVal = getPrintableValue(*vec);
131 kwVal.emplace(kw, printableVal);
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530132 }
133 }
Patrick Williams8be43342021-09-02 09:33:36 -0500134 catch (const sdbusplus::exception::exception& e)
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530135 {
PriyangaRamasamy8cc5b152021-06-03 13:05:17 -0500136 if (string(e.name()) ==
137 string("org.freedesktop.DBus.Error.UnknownObject"))
138 {
139 kwVal.emplace(invPath, json::object({}));
140 objFound = false;
141 break;
142 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530143 }
144 }
145
PriyangaRamasamy8cc5b152021-06-03 13:05:17 -0500146 return kwVal;
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530147}
148
PriyangaRamasamy8cc5b152021-06-03 13:05:17 -0500149void VpdTool::getExtraInterfaceProperties(const string& invPath,
150 const string& extraInterface,
151 const json& prop, json& output)
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530152{
153 variant<string> response;
154
155 string objectName = INVENTORY_PATH + invPath;
156
157 for (const auto& itProp : prop.items())
158 {
159 string kw = itProp.key();
160 try
161 {
162 makeDBusCall(objectName, extraInterface, kw).read(response);
163
164 if (auto str = get_if<string>(&response))
165 {
166 output.emplace(kw, *str);
167 }
168 }
Patrick Williams8be43342021-09-02 09:33:36 -0500169 catch (const sdbusplus::exception::exception& e)
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530170 {
PriyangaRamasamy8cc5b152021-06-03 13:05:17 -0500171 if (std::string(e.name()) ==
172 std::string("org.freedesktop.DBus.Error.UnknownObject"))
173 {
174 objFound = false;
175 break;
176 }
177 else if (std::string(e.name()) ==
178 std::string("org.freedesktop.DBus.Error.UnknownProperty"))
179 {
180 output.emplace(kw, "");
181 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530182 }
183 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530184}
185
186json VpdTool::interfaceDecider(json& itemEEPROM)
187{
188 if (itemEEPROM.find("inventoryPath") == itemEEPROM.end())
189 {
190 throw runtime_error("Inventory Path not found");
191 }
192
193 if (itemEEPROM.find("extraInterfaces") == itemEEPROM.end())
194 {
195 throw runtime_error("Extra Interfaces not found");
196 }
197
PriyangaRamasamy8cc5b152021-06-03 13:05:17 -0500198 json subOutput = json::object({});
Alpana Kumarib6965f12020-06-01 00:32:21 -0500199 fruType = "FRU";
200
PriyangaRamasamy8cc5b152021-06-03 13:05:17 -0500201 json j;
202 objFound = true;
203 string invPath = itemEEPROM.at("inventoryPath");
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530204
PriyangaRamasamy8cc5b152021-06-03 13:05:17 -0500205 j = getVINIProperties(invPath);
206
207 if (objFound)
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530208 {
PriyangaRamasamy8cc5b152021-06-03 13:05:17 -0500209 subOutput.insert(j.begin(), j.end());
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530210 json js;
PriyangaRamasamy8cc5b152021-06-03 13:05:17 -0500211 if (itemEEPROM.find("type") != itemEEPROM.end())
212 {
213 fruType = itemEEPROM.at("type");
214 }
215 js.emplace("TYPE", fruType);
216
217 if (invPath.find("powersupply") != string::npos)
218 {
219 js.emplace("type", POWER_SUPPLY_TYPE_INTERFACE);
220 }
221 else if (invPath.find("fan") != string::npos)
222 {
223 js.emplace("type", FAN_INTERFACE);
224 }
225
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530226 for (const auto& ex : itemEEPROM["extraInterfaces"].items())
227 {
228 if (!(ex.value().is_null()))
229 {
Priyanga Ramasamydacaa472021-10-07 12:22:41 -0500230 // TODO: Remove this if condition check once inventory json is
231 // updated with xyz location code interface.
232 if (ex.key() == "com.ibm.ipzvpd.Location")
233 {
234 getExtraInterfaceProperties(
235 invPath,
236 "xyz.openbmc_project.Inventory.Decorator.LocationCode",
237 ex.value(), js);
238 }
239 else
240 {
241 getExtraInterfaceProperties(invPath, ex.key(), ex.value(),
242 js);
243 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530244 }
PriyangaRamasamy8cc5b152021-06-03 13:05:17 -0500245 if ((ex.key().find("Item") != string::npos) &&
246 (ex.value().is_null()))
247 {
248 js.emplace("type", ex.key());
249 }
250 subOutput.insert(js.begin(), js.end());
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530251 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530252 }
Priyanga Ramasamy0086dd12021-09-28 10:31:50 -0500253 return subOutput;
254}
255
256json VpdTool::getPresentPropJson(const std::string& invPath,
257 std::string& parentPresence)
258{
259 std::variant<bool> response;
260 makeDBusCall(invPath, "xyz.openbmc_project.Inventory.Item", "Present")
261 .read(response);
262
263 std::string presence{};
264
265 if (auto pVal = get_if<bool>(&response))
266 {
267 presence = *pVal ? "true" : "false";
268 if (parentPresence.empty())
269 {
270 parentPresence = presence;
271 }
272 }
273 else
274 {
275 presence = parentPresence;
276 }
277
278 json js;
279 js.emplace("Present", presence);
280 return js;
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530281}
282
283json VpdTool::parseInvJson(const json& jsObject, char flag, string fruPath)
284{
285 json output = json::object({});
286 bool validObject = false;
287
288 if (jsObject.find("frus") == jsObject.end())
289 {
290 throw runtime_error("Frus missing in Inventory json");
291 }
292 else
293 {
294 for (const auto& itemFRUS : jsObject["frus"].items())
295 {
Priyanga Ramasamy0086dd12021-09-28 10:31:50 -0500296 string parentPresence{};
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530297 for (auto itemEEPROM : itemFRUS.value())
298 {
Priyanga Ramasamy0086dd12021-09-28 10:31:50 -0500299 json subOutput = json::object({});
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530300 try
301 {
302 if (flag == 'O')
303 {
304 if (itemEEPROM.find("inventoryPath") ==
305 itemEEPROM.end())
306 {
307 throw runtime_error("Inventory Path not found");
308 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530309 else if (itemEEPROM.at("inventoryPath") == fruPath)
310 {
311 validObject = true;
Priyanga Ramasamy0086dd12021-09-28 10:31:50 -0500312 subOutput = interfaceDecider(itemEEPROM);
313 json presentJs = getPresentPropJson(
314 "/xyz/openbmc_project/inventory" + fruPath,
315 parentPresence);
316 subOutput.insert(presentJs.begin(),
317 presentJs.end());
318 output.emplace(fruPath, subOutput);
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530319 return output;
320 }
Priyanga Ramasamy0086dd12021-09-28 10:31:50 -0500321 else // this else is to keep track of parent present
322 // property.
323 {
324 json presentJs = getPresentPropJson(
325 "/xyz/openbmc_project/inventory" +
326 string(itemEEPROM.at("inventoryPath")),
327 parentPresence);
328 }
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530329 }
330 else
331 {
Priyanga Ramasamy0086dd12021-09-28 10:31:50 -0500332 subOutput = interfaceDecider(itemEEPROM);
333 json presentJs = getPresentPropJson(
334 "/xyz/openbmc_project/inventory" +
335 string(itemEEPROM.at("inventoryPath")),
336 parentPresence);
337 subOutput.insert(presentJs.begin(), presentJs.end());
338 output.emplace(string(itemEEPROM.at("inventoryPath")),
339 subOutput);
340 }
341 }
342 catch (const sdbusplus::exception::SdBusError& e)
343 {
344 // if any of frupath doesn't have Present property of its
345 // own, emplace its parent's present property value.
346 if (e.name() == std::string("org.freedesktop.DBus.Error."
347 "UnknownProperty") &&
348 (((flag == 'O') && validObject) || flag == 'I'))
349 {
350 json presentJs;
351 presentJs.emplace("Present", parentPresence);
352 subOutput.insert(presentJs.begin(), presentJs.end());
353 output.emplace(string(itemEEPROM.at("inventoryPath")),
354 subOutput);
355 }
356
357 // for the user given child frupath which doesn't have
358 // Present prop (vpd-tool -o).
359 if ((flag == 'O') && validObject)
360 {
361 return output;
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530362 }
363 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500364 catch (const exception& e)
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530365 {
366 cerr << e.what();
367 }
368 }
369 }
370 if ((flag == 'O') && (!validObject))
371 {
372 throw runtime_error(
373 "Invalid object path. Refer --dumpInventory/-i option.");
374 }
375 }
376 return output;
377}
378
379void VpdTool::dumpInventory(const nlohmann::basic_json<>& jsObject)
380{
381 char flag = 'I';
PriyangaRamasamycdf943c2020-03-18 02:25:30 +0530382 json output = json::array({});
383 output.emplace_back(parseInvJson(jsObject, flag, ""));
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530384 debugger(output);
385}
386
387void VpdTool::dumpObject(const nlohmann::basic_json<>& jsObject)
388{
389 char flag = 'O';
PriyangaRamasamycdf943c2020-03-18 02:25:30 +0530390 json output = json::array({});
Santosh Puranikd5d52bb2020-07-28 15:08:10 +0530391 output.emplace_back(parseInvJson(jsObject, flag, fruPath));
PriyangaRamasamy1f0b1e62020-02-20 20:48:25 +0530392 debugger(output);
393}
PriyangaRamasamy02d4d4e2020-02-24 14:54:45 +0530394
395void VpdTool::readKeyword()
396{
397 string interface = "com.ibm.ipzvpd.";
398 variant<Binary> response;
399
400 try
401 {
402 json output = json::object({});
403 json kwVal = json::object({});
404 makeDBusCall(INVENTORY_PATH + fruPath, interface + recordName, keyword)
405 .read(response);
406
PriyangaRamasamy887a42a2020-09-03 00:33:57 +0530407 string printableVal{};
PriyangaRamasamy02d4d4e2020-02-24 14:54:45 +0530408 if (auto vec = get_if<Binary>(&response))
409 {
PriyangaRamasamy887a42a2020-09-03 00:33:57 +0530410 printableVal = getPrintableValue(*vec);
PriyangaRamasamy02d4d4e2020-02-24 14:54:45 +0530411 }
PriyangaRamasamy887a42a2020-09-03 00:33:57 +0530412 kwVal.emplace(keyword, printableVal);
PriyangaRamasamy02d4d4e2020-02-24 14:54:45 +0530413
414 output.emplace(fruPath, kwVal);
415
416 debugger(output);
417 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500418 catch (const json::exception& e)
PriyangaRamasamy02d4d4e2020-02-24 14:54:45 +0530419 {
420 json output = json::object({});
421 json kwVal = json::object({});
PriyangaRamasamy02d4d4e2020-02-24 14:54:45 +0530422 }
423}
PriyangaRamasamyd09d2ec2020-03-12 14:11:50 +0530424
425int VpdTool::updateKeyword()
426{
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +0530427 Binary val = toBinary(value);
PriyangaRamasamyd09d2ec2020-03-12 14:11:50 +0530428 auto bus = sdbusplus::bus::new_default();
429 auto properties =
430 bus.new_method_call(BUSNAME, OBJPATH, IFACE, "WriteKeyword");
431 properties.append(static_cast<sdbusplus::message::object_path>(fruPath));
432 properties.append(recordName);
433 properties.append(keyword);
434 properties.append(val);
435 auto result = bus.call(properties);
436
437 if (result.is_method_error())
438 {
439 throw runtime_error("Get api failed");
440 }
441 return 0;
442}
PriyangaRamasamy0407b172020-03-31 13:57:18 +0530443
444void VpdTool::forceReset(const nlohmann::basic_json<>& jsObject)
445{
446 for (const auto& itemFRUS : jsObject["frus"].items())
447 {
448 for (const auto& itemEEPROM : itemFRUS.value().items())
449 {
450 string fru = itemEEPROM.value().at("inventoryPath");
451
452 fs::path fruCachePath = INVENTORY_MANAGER_CACHE;
453 fruCachePath += INVENTORY_PATH;
454 fruCachePath += fru;
455
456 try
457 {
458 for (const auto& it : fs::directory_iterator(fruCachePath))
459 {
460 if (fs::is_regular_file(it.status()))
461 {
462 fs::remove(it);
463 }
464 }
465 }
466 catch (const fs::filesystem_error& e)
467 {
468 }
469 }
470 }
471
PriyangaRamasamy6ee637a2021-02-12 04:49:02 -0600472 cout.flush();
PriyangaRamasamy0407b172020-03-31 13:57:18 +0530473 string udevRemove = "udevadm trigger -c remove -s \"*nvmem*\" -v";
PriyangaRamasamy6ee637a2021-02-12 04:49:02 -0600474 int returnCode = system(udevRemove.c_str());
475 printReturnCode(returnCode);
PriyangaRamasamy0407b172020-03-31 13:57:18 +0530476
477 string invManagerRestart =
478 "systemctl restart xyz.openbmc_project.Inventory.Manager.service";
PriyangaRamasamy6ee637a2021-02-12 04:49:02 -0600479 returnCode = system(invManagerRestart.c_str());
480 printReturnCode(returnCode);
PriyangaRamasamy0407b172020-03-31 13:57:18 +0530481
Santosh Puranik6c7a84e2022-03-09 13:42:18 +0530482 string sysVpdRestart = "systemctl restart system-vpd.service";
483 returnCode = system(sysVpdRestart.c_str());
PriyangaRamasamy6ee637a2021-02-12 04:49:02 -0600484 printReturnCode(returnCode);
PriyangaRamasamy0407b172020-03-31 13:57:18 +0530485
486 string udevAdd = "udevadm trigger -c add -s \"*nvmem*\" -v";
PriyangaRamasamy6ee637a2021-02-12 04:49:02 -0600487 returnCode = system(udevAdd.c_str());
488 printReturnCode(returnCode);
PriyangaRamasamy0407b172020-03-31 13:57:18 +0530489}
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +0530490
491int VpdTool::updateHardware()
492{
493 int rc = 0;
494 bool updCache = true;
495 const Binary& val = static_cast<const Binary&>(toBinary(value));
496 ifstream inventoryJson(INVENTORY_JSON_SYM_LINK);
497 try
498 {
499 auto json = nlohmann::json::parse(inventoryJson);
Santosh Puranika0b23912022-02-10 13:37:09 +0530500 uint32_t offset = 0;
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +0530501 EditorImpl edit(fruPath, json, recordName, keyword);
502 if (!((isPathInJson(fruPath)) &&
503 (isRecKwInDbusJson(recordName, keyword))))
504 {
505 updCache = false;
506 }
Santosh Puranika0b23912022-02-10 13:37:09 +0530507 if (fruPath.starts_with("/sys/bus/spi"))
508 {
509 // TODO: Figure out a better way to get this, SPI eeproms
510 // start at offset 0x30000
511 offset = 0x30000;
512 }
513 edit.updateKeyword(val, offset, updCache);
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +0530514 }
Patrick Williams8e15b932021-10-06 13:04:22 -0500515 catch (const json::parse_error& ex)
PriyangaRamasamyc0a534f2020-08-24 21:29:18 +0530516 {
517 throw(VpdJsonException("Json Parsing failed", INVENTORY_JSON_SYM_LINK));
518 }
519 return rc;
Priyanga Ramasamy38031312021-10-07 16:39:13 -0500520}
521
522void VpdTool::readKwFromHw()
523{
524 uint32_t startOffset = 0;
525
526 ifstream inventoryJson(INVENTORY_JSON_SYM_LINK);
527 auto jsonFile = nlohmann::json::parse(inventoryJson);
528
529 for (const auto& item : jsonFile["frus"][fruPath])
530 {
531 if (item.find("offset") != item.end())
532 {
533 startOffset = item["offset"];
534 }
535 }
536
537 Binary completeVPDFile;
538 completeVPDFile.resize(65504);
539 fstream vpdFileStream;
540 vpdFileStream.open(fruPath,
541 std::ios::in | std::ios::out | std::ios::binary);
542
543 vpdFileStream.seekg(startOffset, ios_base::cur);
544 vpdFileStream.read(reinterpret_cast<char*>(&completeVPDFile[0]), 65504);
545 completeVPDFile.resize(vpdFileStream.gcount());
546 vpdFileStream.clear(std::ios_base::eofbit);
547
548 if (completeVPDFile.empty())
549 {
550 throw std::runtime_error("Invalid File");
551 }
552 Impl obj(completeVPDFile);
553 std::string keywordVal = obj.readKwFromHw(recordName, keyword);
554
555 if (!keywordVal.empty())
556 {
557 json output = json::object({});
558 json kwVal = json::object({});
559 kwVal.emplace(keyword, keywordVal);
560
561 output.emplace(fruPath, kwVal);
562
563 debugger(output);
564 }
565 else
566 {
567 std::cerr << "The given keyword " << keyword << " or record "
568 << recordName
569 << " or both are not present in the given FRU path "
570 << fruPath << std::endl;
571 }
572}