VPD Tool: Update Hardware
Vpd tool has --Hardware/-H flag which should be given along
with writeKeyword flags, if the user wants to write directly to "Hardware".
In general the user should give only the object path in
--path/-P value.
Only if --Hardware/-H flag is given, the user has an
option to give either eeprom path or the object path in
--path/-P value.
Test:
Tested on simics.
./vpd-tool --writeKeyword -H --path < hardware path/object path > -R < record name > -K < keyword > -V < value in hex/ascii >
CASE 1: <updating eeprom path> < update directly on hardware using -H.>
./vpd-tool -u -H -P /sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a480.i2c-bus/i2c-8/8-0051/8-00510/nvmem -R VINI -K PN -V 0x717273
updation successful on both dbus and hardware.
CASE 2:
./vpd-tool -u -H -P /sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a480.i2c-bus/i2c-8/8-0051/8-00510/nvmem -R DINF -K FL -V 0x717273
updation successful on hardware. <this wont get updated in dbus as the given record-keyword pair is not required to update in dbus(only those record keywords
present in dbus_properties.json are required to be updated in dbus).
CASE 3: <failure case - invalid eeprom path>
root@rainier:/tmp# ./vpd-tool -u -H -P /sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a490.i2c-bus/i2c-8/8-0051/8-00510/nvmem -R VINI -K PN -V 0x717273
Invalid object path : /sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e78a490.i2c-bus/i2c-8/8-0051/8-00510/nvmem. Unable to find the corresponding EEPROM path for the given object path : /sys/devices/platform/ahb/ahb:apb/ahb:apb:bus@1e78a000/1e7$
Signed-off-by: PriyangaRamasamy <priyanga24@in.ibm.com>
Change-Id: I6b893e699fe343c90c3a3fd2b07fd8b9a4711687
diff --git a/meson.build b/meson.build
index d5080b3..1a44195 100644
--- a/meson.build
+++ b/meson.build
@@ -47,22 +47,23 @@
'INVENTORY_JSON_SYM_LINK': '"'+get_option('INVENTORY_JSON_SYM_LINK')+'"',
'INVENTORY_JSON_2U': '"'+get_option('INVENTORY_JSON_2U')+'"',
'INVENTORY_JSON_4U': '"'+get_option('INVENTORY_JSON_4U')+'"',
- 'INVENTORY_JSON_EVEREST': '"'+get_option('INVENTORY_JSON_EVEREST')+'"'
+ 'INVENTORY_JSON_EVEREST': '"'+get_option('INVENTORY_JSON_EVEREST')+'"',
+ 'DBUS_PROP_JSON': '"'+get_option('DBUS_PROP_JSON')+'"'
}
)
+common_SOURCES =[
+'vpd-parser/parser_factory.cpp',
+ 'vpd-parser/memory_vpd_parser.cpp',
+ 'vpd-parser/keyword_vpd_parser.cpp',
+ 'vpd-parser/ipz_parser.cpp', 'impl.cpp', 'utils.cpp',
+ 'vpdecc/vpdecc.c', 'vpdecc/vpdecc_support.c'
+]
+
if get_option('ibm-parser').enabled()
libgpiodcxx = dependency('libgpiodcxx')
- ibm_read_vpd_SOURCES = ['ibm_vpd_app.cpp',
- 'vpd-parser/ipz_parser.cpp',
- 'impl.cpp',
- 'utils.cpp',
- 'vpd-parser/keyword_vpd_parser.cpp',
- 'vpdecc/vpdecc.c',
- 'vpdecc/vpdecc_support.c',
- 'vpd-parser/memory_vpd_parser.cpp',
- 'vpd-parser/parser_factory.cpp'
- ]
+ ibm_read_vpd_SOURCES = ['ibm_vpd_app.cpp'
+ ]+common_SOURCES
ibm_vpd_exe = executable(
'ibm-read-vpd',
@@ -78,8 +79,11 @@
)
vpd_tool_SOURCES = ['vpd_tool.cpp',
- 'vpd_tool_impl.cpp'
- ]
+ 'vpd_tool_impl.cpp',
+ 'vpd-manager/editor_impl.cpp',
+ ]+common_SOURCES
+
+ vpd_tool_INCLUDE = include_directories('vpd-parser/', 'vpd-manager')
vpd_tool_exe = executable(
'vpd-tool',
@@ -87,7 +91,8 @@
dependencies: [
sdbusplus
],
- install: true
+ install: true,
+ include_directories : vpd_tool_INCLUDE
)
if get_option('vpd-manager').enabled()
subdir('vpd-manager')
diff --git a/meson_options.txt b/meson_options.txt
index 46adce4..f5c839c 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -20,3 +20,4 @@
option('INVENTORY_JSON_2U',type: 'string', value: '/usr/share/vpd/50001001.json', description: 'Inventory JSON for 2U system.')
option('INVENTORY_JSON_4U',type: 'string', value: '/usr/share/vpd/50001000.json', description: 'Inventory JSON for 4U system.')
option('INVENTORY_JSON_EVEREST',type: 'string', value: '/usr/share/vpd/50003000.json', description: 'Inventory JSON for Everest system.')
+option('DBUS_PROP_JSON',type: 'string', value: '/usr/share/vpd/dbus_properties.json', description: 'Json which contains properties specific to dbus.')
diff --git a/test/vpd-manager-test/editor_test.cpp b/test/vpd-manager-test/editor_test.cpp
index 2f7114a..1549df8 100644
--- a/test/vpd-manager-test/editor_test.cpp
+++ b/test/vpd-manager-test/editor_test.cpp
@@ -87,7 +87,7 @@
{
// Invalid kwd name
EditorImpl edit("VINI", "SN", std::move(emptyVpdFile));
- edit.updateKeyword(dataToUodate);
+ edit.updateKeyword(dataToUodate, true);
}
catch (const std::exception& e)
{
@@ -105,7 +105,7 @@
{
// the path is dummy
EditorImpl edit("VINI", "SN", std::move(vpd));
- edit.updateKeyword(dataToUodate);
+ edit.updateKeyword(dataToUodate, true);
}
catch (const std::exception& e)
{
@@ -124,7 +124,7 @@
{
// Invalid record name "VIN", path is dummy
EditorImpl edit("VIN", "SN", std::move(vpd));
- edit.updateKeyword(dataToUodate);
+ edit.updateKeyword(dataToUodate, true);
}
catch (const std::exception& e)
{
@@ -143,7 +143,7 @@
{
// All valid data
EditorImpl edit("VINI", "Sn", std::move(vpd));
- edit.updateKeyword(dataToUodate);
+ edit.updateKeyword(dataToUodate, true);
}
catch (std::runtime_error& e)
{
@@ -162,7 +162,7 @@
{
// All valid data
EditorImpl edit("VINI", "SN", std::move(vpd));
- edit.updateKeyword(dataToUodate);
+ edit.updateKeyword(dataToUodate, true);
}
catch (std::runtime_error& e)
{
@@ -176,4 +176,4 @@
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
-}
\ No newline at end of file
+}
diff --git a/utils.cpp b/utils.cpp
index b9d07e4..69fbe6e 100644
--- a/utils.cpp
+++ b/utils.cpp
@@ -3,12 +3,19 @@
#include "utils.hpp"
#include "defines.hpp"
+#include "vpd_exceptions.hpp"
+#include <fstream>
+#include <iomanip>
#include <phosphor-logging/elog-errors.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/server.hpp>
+#include <sstream>
+#include <vector>
#include <xyz/openbmc_project/Common/error.hpp>
+using json = nlohmann::json;
+
namespace openpower
{
namespace vpd
@@ -17,7 +24,8 @@
using namespace inventory;
using namespace phosphor::logging;
using namespace sdbusplus::xyz::openbmc_project::Common::Error;
-
+using namespace record;
+using namespace openpower::vpd::exceptions;
namespace inventory
{
@@ -230,5 +238,113 @@
"Error in invoking D-Bus logging create interface to register PEL");
}
}
+
+inventory::VPDfilepath getVpdFilePath(const string& jsonFile,
+ const std::string& ObjPath)
+{
+ ifstream inventoryJson(jsonFile);
+ const auto& jsonObject = json::parse(inventoryJson);
+ inventory::VPDfilepath filePath{};
+
+ if (jsonObject.find("frus") == jsonObject.end())
+ {
+ throw(VpdJsonException(
+ "Invalid JSON structure - frus{} object not found in ", jsonFile));
+ }
+
+ const nlohmann::json& groupFRUS =
+ jsonObject["frus"].get_ref<const nlohmann::json::object_t&>();
+ for (const auto& itemFRUS : groupFRUS.items())
+ {
+ const std::vector<nlohmann::json>& groupEEPROM =
+ itemFRUS.value().get_ref<const nlohmann::json::array_t&>();
+ for (const auto& itemEEPROM : groupEEPROM)
+ {
+ if (itemEEPROM["inventoryPath"]
+ .get_ref<const nlohmann::json::string_t&>() == ObjPath)
+ {
+ filePath = itemFRUS.key();
+ return filePath;
+ }
+ }
+ }
+
+ return filePath;
+}
+
+bool isPathInJson(const std::string& eepromPath)
+{
+ bool present = false;
+ ifstream inventoryJson(INVENTORY_JSON_SYM_LINK);
+
+ try
+ {
+ auto js = json::parse(inventoryJson);
+ if (js.find("frus") == js.end())
+ {
+ throw(VpdJsonException(
+ "Invalid JSON structure - frus{} object not found in ",
+ INVENTORY_JSON_SYM_LINK));
+ }
+ json fruJson = js["frus"];
+
+ if (fruJson.find(eepromPath) != fruJson.end())
+ {
+ present = true;
+ }
+ }
+ catch (json::parse_error& ex)
+ {
+ throw(VpdJsonException("Json Parsing failed", INVENTORY_JSON_SYM_LINK));
+ }
+ return present;
+}
+
+bool isRecKwInDbusJson(const std::string& recordName,
+ const std::string& keyword)
+{
+ ifstream propertyJson(DBUS_PROP_JSON);
+ json dbusProperty;
+ bool present = false;
+
+ if (propertyJson.is_open())
+ {
+ try
+ {
+ auto dbusPropertyJson = json::parse(propertyJson);
+ if (dbusPropertyJson.find("dbusProperties") ==
+ dbusPropertyJson.end())
+ {
+ throw(VpdJsonException("dbusProperties{} object not found in "
+ "DbusProperties json : ",
+ DBUS_PROP_JSON));
+ }
+
+ dbusProperty = dbusPropertyJson["dbusProperties"];
+ if (dbusProperty.contains(recordName))
+ {
+ const vector<string>& kwdsToPublish = dbusProperty[recordName];
+ if (find(kwdsToPublish.begin(), kwdsToPublish.end(), keyword) !=
+ kwdsToPublish.end()) // present
+ {
+ present = true;
+ }
+ }
+ }
+ catch (json::parse_error& ex)
+ {
+ throw(VpdJsonException("Json Parsing failed", DBUS_PROP_JSON));
+ }
+ }
+ else
+ {
+ // If dbus properties json is not available, we assume the given
+ // record-keyword is part of dbus-properties json. So setting the bool
+ // variable to true.
+ present = true;
+ }
+ return present;
+}
+
} // namespace vpd
} // namespace openpower
diff --git a/utils.hpp b/utils.hpp
index 2c748ee..94f4992 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -4,6 +4,7 @@
#include "types.hpp"
#include <iostream>
+#include <nlohmann/json.hpp>
using namespace std;
@@ -108,5 +109,35 @@
void createPEL(const std::map<std::string, std::string>& additionalData,
const std::string& errIntf);
+/**
+ * @brief getVpdFilePath
+ * Get vpd file path corresponding to the given object path.
+ * @param[in] - json file path
+ * @param[in] - Object path
+ * @return - Vpd file path
+ */
+inventory::VPDfilepath getVpdFilePath(const string& jsonFile,
+ const std::string& ObjPath);
+
+/**
+ * @brief isPathInJson
+ * API which checks for the presence of the given eeprom path in the given json.
+ * @param[in] - eepromPath
+ * @return - true if the eeprom is present in the json; false otherwise
+ */
+bool isPathInJson(const std::string& eepromPath);
+
+/**
+ * @brief isRecKwInDbusJson
+ * API which checks whether the given keyword under the given record is to be
+ * published on dbus or not. Checks against the keywords present in
+ * dbus_property.json.
+ * @param[in] - record name
+ * @param[in] - keyword name
+ * @return - true if the record-keyword pair is present in dbus_property.json;
+ * false otherwise.
+ */
+bool isRecKwInDbusJson(const std::string& record, const std::string& keyword);
+
} // namespace vpd
} // namespace openpower
diff --git a/vpd-manager/editor_impl.cpp b/vpd-manager/editor_impl.cpp
index a20cd44..9ca68fc 100644
--- a/vpd-manager/editor_impl.cpp
+++ b/vpd-manager/editor_impl.cpp
@@ -578,7 +578,7 @@
}
}
-void EditorImpl::updateKeyword(const Binary& kwdData)
+void EditorImpl::updateKeyword(const Binary& kwdData, const bool& updCache)
{
startOffset = 0;
#ifndef ManagerTest
@@ -649,9 +649,13 @@
// update the ECC data for the record once data has been updated
updateRecordECC();
+
#ifndef ManagerTest
- // update the cache once data has been updated
- updateCache();
+ if (updCache)
+ {
+ // update the cache once data has been updated
+ updateCache();
+ }
#endif
}
catch (const std::exception& e)
@@ -665,7 +669,6 @@
return;
}
}
-
} // namespace editor
} // namespace manager
} // namespace vpd
diff --git a/vpd-manager/editor_impl.hpp b/vpd-manager/editor_impl.hpp
index 9cf3b8a..96cfd07 100644
--- a/vpd-manager/editor_impl.hpp
+++ b/vpd-manager/editor_impl.hpp
@@ -75,10 +75,25 @@
{
}
+ /** @brief Construct EditorImpl class
+ *
+ * @param[in] path - EEPROM path
+ * @param[in] json - Parsed inventory json object
+ * @param[in] record - Record name
+ * @param[in] kwd - Keyword name
+ */
+ EditorImpl(const inventory::Path& path, const nlohmann::json& json,
+ const std::string& record, const std::string& kwd) :
+ vpdFilePath(path),
+ jsonFile(json), thisRecord(record, kwd)
+ {
+ }
+
/** @brief Update data for keyword
* @param[in] kwdData - data to update
+ * @param[in] updCache - Flag which tells whether to update Cache or not.
*/
- void updateKeyword(const Binary& kwdData);
+ void updateKeyword(const Binary& kwdData, const bool& updCache);
/** @brief Expands location code on DBUS
* @param[in] locationCodeType - "fcs" or "mts"
diff --git a/vpd-manager/manager.cpp b/vpd-manager/manager.cpp
index 606d3b5..5344664 100644
--- a/vpd-manager/manager.cpp
+++ b/vpd-manager/manager.cpp
@@ -109,7 +109,7 @@
// instantiate editor class to update the data
EditorImpl edit(vpdFilePath, jsonFile, recordName, keyword, path);
- edit.updateKeyword(value);
+ edit.updateKeyword(value, true);
// if it is a motehrboard FRU need to check for location expansion
if (frus.find(path)->second.second)
diff --git a/vpd_tool.cpp b/vpd_tool.cpp
index e0e010a..9e76331 100644
--- a/vpd_tool.cpp
+++ b/vpd_tool.cpp
@@ -1,11 +1,16 @@
+#include "utils.hpp"
#include "vpd_tool_impl.hpp"
#include <CLI/CLI.hpp>
+#include <filesystem>
#include <fstream>
#include <iostream>
using namespace CLI;
using namespace std;
+namespace fs = std::filesystem;
+using namespace openpower::vpd;
+using json = nlohmann::json;
int main(int argc, char** argv)
{
@@ -17,6 +22,7 @@
string recordName{};
string keyword{};
string val{};
+ string path{};
auto object =
app.add_option("--object, -O", objectPath, "Enter the Object Path");
@@ -27,6 +33,10 @@
"--value, -V", val,
"Enter the value. The value to be updated should be either in ascii or "
"in hex. ascii eg: 01234; hex eg: 0x30313233");
+ auto pathOption =
+ app.add_option("--path, -P", path,
+ "Path - if hardware option is used, give either EEPROM "
+ "path/Object path; if not give the object path");
auto dumpObjFlag =
app.add_flag("--dumpObject, -o",
@@ -55,7 +65,7 @@
"--writeKeyword/-w/--updateKeyword/-u "
"--object/-O object-name --record/-R record-name --keyword/-K "
"keyword-name --value/-V value-to-be-updated }")
- ->needs(object)
+ ->needs(pathOption)
->needs(record)
->needs(kw)
->needs(valOption);
@@ -63,6 +73,12 @@
auto forceResetFlag = app.add_flag(
"--forceReset, -f, -F", "Force Collect for Hardware. { vpd-tool-exe "
"--forceReset/-f/-F }");
+ auto Hardware = app.add_flag(
+ "--Hardware, -H",
+ "This is a supplementary flag to read/write directly from/to hardware. "
+ "Enter the hardware path while using the object option in "
+ "corresponding read/write flags. This --Hardware flag is to be given "
+ "along with readKeyword/writeKeyword.");
CLI11_PARSE(app, argc, argv);
@@ -71,6 +87,28 @@
try
{
+ if (*Hardware)
+ {
+ if (!fs::exists(path)) // dbus object path
+ {
+ string p = getVpdFilePath(INVENTORY_JSON_SYM_LINK, path);
+ if (p.empty()) // object path not present in inventory json
+ {
+ string errorMsg = "Invalid object path : ";
+ errorMsg += path;
+ errorMsg += ". Unable to find the corresponding EEPROM "
+ "path for the given object path : ";
+ errorMsg += path;
+ errorMsg += " in the vpd inventory json : ";
+ errorMsg += INVENTORY_JSON_SYM_LINK;
+ throw runtime_error(errorMsg);
+ }
+ else
+ {
+ path = p;
+ }
+ }
+ }
if (*dumpObjFlag)
{
VpdTool vpdToolObj(move(objectPath));
@@ -90,10 +128,10 @@
vpdToolObj.readKeyword();
}
- else if (*writeFlag)
+ else if (*writeFlag && !*Hardware)
{
- VpdTool vpdToolObj(move(objectPath), move(recordName),
- move(keyword), move(val));
+ VpdTool vpdToolObj(move(path), move(recordName), move(keyword),
+ move(val));
rc = vpdToolObj.updateKeyword();
}
@@ -103,6 +141,13 @@
vpdToolObj.forceReset(jsObject);
}
+ else if (*writeFlag && *Hardware)
+ {
+ VpdTool vpdToolObj(move(path), move(recordName), move(keyword),
+ move(val));
+ rc = vpdToolObj.updateHardware();
+ }
+
else
{
throw runtime_error("One of the valid options is required. Refer "
diff --git a/vpd_tool_impl.cpp b/vpd_tool_impl.cpp
index 5ffb041..647bf50 100644
--- a/vpd_tool_impl.cpp
+++ b/vpd_tool_impl.cpp
@@ -1,18 +1,52 @@
#include "vpd_tool_impl.hpp"
+#include "vpd_exceptions.hpp"
+
#include <cstdlib>
#include <filesystem>
-#include <iomanip>
#include <iostream>
#include <sdbusplus/bus.hpp>
-#include <sstream>
#include <variant>
#include <vector>
using namespace std;
using sdbusplus::exception::SdBusError;
using namespace openpower::vpd;
+using namespace inventory;
+using namespace openpower::vpd::manager::editor;
namespace fs = std::filesystem;
+using json = nlohmann::json;
+using namespace openpower::vpd::exceptions;
+
+Binary VpdTool::toBinary(const std::string& value)
+{
+ Binary val{};
+ if (value.find("0x") == string::npos)
+ {
+ val.assign(value.begin(), value.end());
+ }
+ else if (value.find("0x") != string::npos)
+ {
+ stringstream ss;
+ ss.str(value.substr(2));
+ string byteStr{};
+
+ while (!ss.eof())
+ {
+ ss >> setw(2) >> byteStr;
+ uint8_t byte = strtoul(byteStr.c_str(), nullptr, 16);
+
+ val.push_back(byte);
+ }
+ }
+
+ else
+ {
+ throw runtime_error("The value to be updated should be either in ascii "
+ "or in hex. Refer --help option");
+ }
+ return val;
+}
void VpdTool::printReturnCode(int returnCode)
{
@@ -351,35 +385,7 @@
int VpdTool::updateKeyword()
{
- Binary val;
-
- if (value.find("0x") == string::npos)
- {
- val.assign(value.begin(), value.end());
- }
- else if (value.find("0x") != string::npos)
- {
- stringstream ss;
- ss.str(value.substr(2));
- string byteStr{};
-
- while (!ss.eof())
- {
- ss >> setw(2) >> byteStr;
- uint8_t byte = strtoul(byteStr.c_str(), nullptr, 16);
-
- val.push_back(byte);
- }
- }
-
- else
- {
- throw runtime_error("The value to be updated should be either in ascii "
- "or in hex. Refer --help option");
- }
-
- // writeKeyword(fruPath, recordName, keyword, val);
-
+ Binary val = toBinary(value);
auto bus = sdbusplus::bus::new_default();
auto properties =
bus.new_method_call(BUSNAME, OBJPATH, IFACE, "WriteKeyword");
@@ -442,3 +448,27 @@
returnCode = system(udevAdd.c_str());
printReturnCode(returnCode);
}
+
+int VpdTool::updateHardware()
+{
+ int rc = 0;
+ bool updCache = true;
+ const Binary& val = static_cast<const Binary&>(toBinary(value));
+ ifstream inventoryJson(INVENTORY_JSON_SYM_LINK);
+ try
+ {
+ auto json = nlohmann::json::parse(inventoryJson);
+ EditorImpl edit(fruPath, json, recordName, keyword);
+ if (!((isPathInJson(fruPath)) &&
+ (isRecKwInDbusJson(recordName, keyword))))
+ {
+ updCache = false;
+ }
+ edit.updateKeyword(val, updCache);
+ }
+ catch (json::parse_error& ex)
+ {
+ throw(VpdJsonException("Json Parsing failed", INVENTORY_JSON_SYM_LINK));
+ }
+ return rc;
+}
diff --git a/vpd_tool_impl.hpp b/vpd_tool_impl.hpp
index b490fcc..1ca50b5 100644
--- a/vpd_tool_impl.hpp
+++ b/vpd_tool_impl.hpp
@@ -1,6 +1,8 @@
#include "config.h"
+#include "editor_impl.hpp"
#include "types.hpp"
+#include "utils.hpp"
#include <nlohmann/json.hpp>
#include <string>
@@ -118,9 +120,22 @@
*/
void eraseInventoryPath(std::string& fru);
- /** @brief printReturnCode */
+ /**
+ * @brief printReturnCode
+ * Prints the return code of the program in console output, whenever
+ * the program fails to exit successfully.
+ *
+ * @param[in] returnCode - return code of the program.
+ */
void printReturnCode(int returnCode);
+ /**
+ * @brief Convert hex/ascii values to Binary
+ * @param[in] - value in hex/ascii.
+ * @param[out] - value in binary.
+ */
+ openpower::vpd::Binary toBinary(const std::string& value);
+
public:
/**
* @brief Dump the complete inventory in JSON format
@@ -166,13 +181,22 @@
* @brief Get Printable Value
*
* Checks if the vector value has non printable characters.
- * And returns hex value if non printable char is found else
+ * Returns hex value if non printable char is found else
* returns ascii value.
*
* @param[in] vector - Reference of the Binary vector
* @return printable value - either in hex or in ascii.
*/
- std::string getPrintableValue(const std::vector<unsigned char>& vec);
+ std::string getPrintableValue(const std::vector<unsigned char>& vector);
+
+ /**
+ * @brief Update Hardware
+ * If the given record-keyword pair is present in dbus_properties.json,
+ * then will update the given data in both dbus and hardware.
+ * Else update the given data only in hardware.
+ * @return returncode (success/failure).
+ */
+ int updateHardware();
/**
* @brief Constructor