vpd-tool: Fix for Hardware write option

With this commit, vpd-tool has a fix in Hardware (-H) write
option to update only in hardware and not in cache.

This commit also has an added change. User can provide
VPD offset(--seek) value while using read from hardware and write
to hardware options. Providing offset value(--seek) is optional.
By default offset value is 0. This option is mainly usefull
when providing an EEPROM path which is not present in VPD json.

Test: Tested on everest.

CASE 1: Using Hardware write option will update only hardware and not cache.

root@ever20bmc:/tmp# ./vpd-tool -w -H -O /sys/bus/spi/drivers/at25/spi12.0/eeprom  -R VINI -K CC --seek 0x30000 -V 0x32

root@ever20bmc:/tmp# ./vpd-tool -r -H -O /sys/bus/spi/drivers/at25/spi12.0/eeprom -R VINI -K CC --seek 0x30000
{
    "/sys/bus/spi/drivers/at25/spi12.0/eeprom": {
        "CC": "2C5F"
    }
}
root@ever20bmc:/tmp# ./vpd-tool -r -O /system/chassis/motherboard/dcm0/cpu0 -R VINI -K CC
{
    "/system/chassis/motherboard/dcm0/cpu1": {
        "CC": "5C5F"
    }
}

CASE 2: Using normal write option will update both hardware and cache

root@ever20bmc:/tmp# ./vpd-tool -w -O /system/chassis/motherboard/dcm0/cpu1 -R VINI -K CC -V 0x64

root@ever20bmc:/tmp# ./vpd-tool -r -O /system/chassis/motherboard/dcm0/cpu1  -R VINI -K CC
{
    "/system/chassis/motherboard/dcm0/cpu1": {
        "CC": "dC5F"
    }
}
root@ever20bmc:/tmp# ./vpd-tool -r -H -O /sys/bus/spi/drivers/at25/spi22.0/eeprom -R VINI -K CC --seek 0x30000

{
    "/sys/bus/spi/drivers/at25/spi22.0/eeprom": {
        "CC": "dC5F"
    }
}

CASE 3: Providing wrong offset value during hardware read

root@ever20bmc:/tmp# ./vpd-tool -r -H -O /sys/bus/i2c/drivers/at24/28-0051/eeprom -R VINI -K CC --seek 19
VHDR record not found
Did you provide a valid offset? By default VPD offset is taken as 0. To input offset, use --offset. Refer vpd-tool help.

CASE 4: Providing offset value during normal read operation. As mentioned, there'll be no impact as the offset value will be
valid only while using --Hardware/-H option.

root@ever20bmc:/tmp# ./vpd-tool -r -O /system/chassis/motherboard/dcm0/cpu1  -R VINI -K CC --seek 0
{
    "/system/chassis/motherboard/dcm0/cpu1": {
        "CC": "5C5F"
    }
}

CASE 5: Providing wrong offset value during hardware write

root@ever20bmc:/tmp# ./vpd-tool -w -H -O /sys/bus/spi/drivers/at25/spi12.0/eeprom -R VINI -K CC --seek 20 -V 0x53
Could not find start tag in VPD /sys/bus/spi/drivers/at25/spi12.0/eeprom
Did you provide a valid offset? By default VPD offset is taken as 0. To input offset, use --seek. Refer vpd-tool help.

CASE 6: Providing offset value during normal write. As mentioned, there'll be no impact as the offset value will be valid
only while using --Hardware/-H option.

root@ever8bmc:/tmp# ./vpd-tool -w -O /system/chassis/motherboard/dasd_backplane/panel1 -R VMPU -K VZ -V 0x64 --seek 19
root@ever8bmc:/tmp# ./vpd-tool -r -O /system/chassis/motherboard/dasd_backplane/panel1 -R VMPU -K VZ
{
    "/system/chassis/motherboard/dasd_backplane/panel1": {
        "VZ": "d1"
    }
}

CASE 7: Reading and Writing on a EEPROM path which is not a key in JSON.

root@ever8bmc:/tmp# ./vpd-tool -w -O /sys/bus/spi/drivers/at25/spi13.0/eeprom -R VMPU -K VZ --seek 196608 -H -V 0x64
root@ever8bmc:/tmp#
root@ever8bmc:/tmp# ./vpd-tool -r -O /sys/bus/spi/drivers/at25/spi13.0/eeprom -R VMPU -K VZ --seek 196608 -H
{
    "/sys/bus/spi/drivers/at25/spi13.0/eeprom": {
        "VZ": "d1"
    }
}

Signed-off-by: Priyanga Ramasamy <priyanga24@in.ibm.com>
Change-Id: Icc94b5ee0d044271acf7b6263405fca07eb33728
diff --git a/vpd-manager/editor_impl.cpp b/vpd-manager/editor_impl.cpp
index 30f0a41..e0f13d3 100644
--- a/vpd-manager/editor_impl.cpp
+++ b/vpd-manager/editor_impl.cpp
@@ -6,6 +6,7 @@
 #include "ibm_vpd_utils.hpp"
 #include "ipz_parser.hpp"
 #include "parser_factory.hpp"
+#include "vpd_exceptions.hpp"
 
 #include "vpdecc/vpdecc.h"
 
@@ -575,6 +576,11 @@
         }
         return;
     }
+    else
+    {
+        throw openpower::vpd::exceptions::VpdDataException(
+            "Could not find start tag in VPD " + vpdFilePath);
+    }
 }
 } // namespace editor
 } // namespace manager
diff --git a/vpd_tool.cpp b/vpd_tool.cpp
index 74c12ac..740d5cd 100644
--- a/vpd_tool.cpp
+++ b/vpd_tool.cpp
@@ -21,6 +21,7 @@
     string recordName{};
     string keyword{};
     string val{};
+    uint32_t offset = 0;
 
     auto object =
         app.add_option("--object, -O", objectPath, "Enter the Object Path");
@@ -31,6 +32,11 @@
         "--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");
+    app.add_option("--seek, -s", offset,
+                   "User can provide VPD offset using this option. Default "
+                   "offset value is 0. Using --offset is optional and is valid "
+                   "only while using --Hardware/-H option.");
+
     auto dumpObjFlag =
         app.add_flag("--dumpObject, -o",
                      "Dump the given object from the inventory. { "
@@ -63,14 +69,16 @@
             ->needs(kw)
             ->needs(valOption);
 
-    auto forceResetFlag = app.add_flag(
-        "--forceReset, -f, -F", "Force Collect for Hardware. { vpd-tool-exe "
-                                "--forceReset/-f/-F }");
+    auto forceResetFlag =
+        app.add_flag("--forceReset, -f, -F",
+                     "Force Collect for Hardware. CAUTION: Developer Only "
+                     "Option. { vpd-tool-exe --forceReset/-f/-F }");
+
     auto Hardware = app.add_flag(
         "--Hardware, -H",
-        "This is a supplementary flag to write directly to hardware. When the "
-        "-H flag is given, User should provide valid hardware/eeprom path (and "
-        "not dbus object path) in the -O/--object path.");
+        "This is a supplementary flag to read/write directly from/to hardware. "
+        "User should provide valid hardware/eeprom path (and not dbus object "
+        "path) in the -O/--object path. CAUTION: Developer Only Option");
 
     CLI11_PARSE(app, argc, argv);
 
@@ -139,13 +147,13 @@
         {
             VpdTool vpdToolObj(move(objectPath), move(recordName),
                                move(keyword), move(val));
-            rc = vpdToolObj.updateHardware();
+            rc = vpdToolObj.updateHardware(offset);
         }
         else if (*readFlag && *Hardware)
         {
             VpdTool vpdToolObj(move(objectPath), move(recordName),
                                move(keyword));
-            vpdToolObj.readKwFromHw();
+            vpdToolObj.readKwFromHw(offset);
         }
         else
         {
@@ -157,6 +165,13 @@
     catch (const exception& e)
     {
         cerr << e.what();
+
+        if (*Hardware)
+        {
+            std::cerr << "\nDid you provide a valid offset? By default VPD "
+                         "offset is taken as 0. To input offset, use --offset. "
+                         "Refer vpd-tool help.";
+        }
         rc = -1;
     }
 
diff --git a/vpd_tool_impl.cpp b/vpd_tool_impl.cpp
index c52511f..0d57351 100644
--- a/vpd_tool_impl.cpp
+++ b/vpd_tool_impl.cpp
@@ -488,29 +488,17 @@
     printReturnCode(returnCode);
 }
 
-int VpdTool::updateHardware()
+int VpdTool::updateHardware(const uint32_t offset)
 {
     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);
-        uint32_t offset = 0;
         EditorImpl edit(fruPath, json, recordName, keyword);
-        if (!((isPathInJson(fruPath)) &&
-              (isRecKwInDbusJson(recordName, keyword))))
-        {
-            updCache = false;
-        }
-        if (fruPath.starts_with("/sys/bus/spi"))
-        {
-            // TODO: Figure out a better way to get this, SPI eeproms
-            // start at offset 0x30000
-            offset = 0x30000;
-        }
-        edit.updateKeyword(val, offset, updCache);
+
+        edit.updateKeyword(val, offset, false);
     }
     catch (const json::parse_error& ex)
     {
@@ -519,21 +507,11 @@
     return rc;
 }
 
-void VpdTool::readKwFromHw()
+void VpdTool::readKwFromHw(const uint32_t& startOffset)
 {
-    uint32_t startOffset = 0;
-
     ifstream inventoryJson(INVENTORY_JSON_SYM_LINK);
     auto jsonFile = nlohmann::json::parse(inventoryJson);
 
-    for (const auto& item : jsonFile["frus"][fruPath])
-    {
-        if (item.find("offset") != item.end())
-        {
-            startOffset = item["offset"];
-        }
-    }
-
     Binary completeVPDFile;
     completeVPDFile.resize(65504);
     fstream vpdFileStream;
diff --git a/vpd_tool_impl.hpp b/vpd_tool_impl.hpp
index 8b1207d..3de98f9 100644
--- a/vpd_tool_impl.hpp
+++ b/vpd_tool_impl.hpp
@@ -175,20 +175,28 @@
 
     /**
      * @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.
+     * The given data is updated only on the given hardware path and not on dbus
+     * for the given record-keyword pair. The user can now update record-keyword
+     * value for any hardware path irrespective of whether its present or not in
+     * VPD JSON, by providing a valid offset. By default offset takes 0.
+     *
+     * @param[in] offset - VPD offset.
      * @return returncode (success/failure).
      */
-    int updateHardware();
+    int updateHardware(const uint32_t offset);
 
     /**
      * @brief Read Keyword from Hardware
      * This api is to read a keyword directly from the hardware. The hardware
      * path, record name and keyword name are received at the time of
      * initialising the constructor.
+     * The user can now read keyword from any hardware path irrespective of
+     * whether its present or not in VPD JSON, by providing a valid offset. By
+     * default offset takes 0.
+     *
+     * @param[in] startOffset - VPD offset.
      */
-    void readKwFromHw();
+    void readKwFromHw(const uint32_t& startOffset);
 
     /**
      * @brief Constructor