Enable MemoryLocation properties to be customized

-Enable MemoryLocation properties to be customized by a json table.
Example of memoryLocationTable.json:
{
    "<MemoryDeviceLocator>": {
      "MemoryController": 1,
      "Socket":48,
      "Slot":0,
      "Channel":0
    }
}

-Add attribute "memoryController", "slot", "channel", "socket" in
MemoryLocation.

Tested:

memoryLocationTable.json:
{
    "DIMM4": {
      "MemoryController": 1,
      "Socket":48,
      "Slot":0,
      "Channel":0
    }
}

with the change:
curl -k -X GET http://${bmc}/redfish/v1/Systems/system/Memory/dimm1
{
  "@odata.id": "/redfish/v1/Systems/system/Memory/dimm1",
  "@odata.type": "#Memory.v1_11_0.Memory",
  "AllowedSpeedsMHz": [],
  "BaseModuleType": "RDIMM",
  "BusWidthBits": 80,
  "CapacityMiB": 49152,
  "DataWidthBits": 64,
  "ErrorCorrection": "SingleBitECC",
  "FirmwareRevision": "0",
  "Id": "dimm1",
  "Location": {
    "PartLocation": {
      "LocationType": "Slot",
      "ServiceLabel": "DIMM4"
    }
  },
  "Manufacturer": "",
  "MemoryDeviceType": "DDR5",
  "MemoryLocation": {
    "Channel": 0,
    "MemoryController": 1,
    "Slot": 0,
    "Socket": 48
  }

without the change:

curl -k -X GET http://${bmc}/redfish/v1/Systems/system/Memory/dimm1
{
  "@odata.id": "/redfish/v1/Systems/system/Memory/dimm1",
  "@odata.type": "#Memory.v1_11_0.Memory",
  "AllowedSpeedsMHz": [],
  "BaseModuleType": "RDIMM",
  "BusWidthBits": 80,
  "CapacityMiB": 49152,
  "DataWidthBits": 64,
  "ErrorCorrection": "SingleBitECC",
  "FirmwareRevision": "0",
  "Id": "dimm1",
  "Location": {
    "PartLocation": {
      "LocationType": "Slot",
      "ServiceLabel": "DIMM4"
    }
  },
  "Manufacturer": "",
  "MemoryDeviceType": "DDR5",
  "MemoryLocation": {
    "Channel": 0,
    "MemoryController": 0,
    "Slot": 0,
    "Socket": 0
  }

Change-Id: Ic5ff09715a619907f06d06af33aa0d1755c8b4f3
Signed-off-by: Tony Lee <tony.lee@quantatw.com>
diff --git a/include/dimm.hpp b/include/dimm.hpp
index a3a48be..e848f42 100644
--- a/include/dimm.hpp
+++ b/include/dimm.hpp
@@ -17,6 +17,7 @@
 #pragma once
 #include "smbios_mdrv2.hpp"
 
+#include <nlohmann/json.hpp>
 #include <xyz/openbmc_project/Association/Definitions/server.hpp>
 #include <xyz/openbmc_project/Inventory/Connector/Slot/server.hpp>
 #include <xyz/openbmc_project/Inventory/Decorator/Asset/server.hpp>
@@ -41,6 +42,8 @@
 using MemoryTechType =
     sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::MemoryTech;
 
+using Json = nlohmann::json;
+
 class Dimm :
     sdbusplus::server::object_t<
         sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm>,
@@ -119,9 +122,12 @@
     MemoryTechType memoryMedia(MemoryTechType value) override;
     uint8_t slot(uint8_t value) override;
     uint8_t socket(uint8_t value) override;
+    uint8_t memoryController(uint8_t value) override;
+    uint8_t channel(uint8_t value) override;
     uint16_t memoryConfiguredSpeedInMhz(uint16_t value) override;
     bool functional(bool value) override;
     EccType ecc(EccType value) override;
+    Json parseConfigFile();
 
   private:
     uint8_t dimmNum;
@@ -250,6 +256,14 @@
     {0x5, MemoryTechType::NVDIMM_F},   {0x6, MemoryTechType::NVDIMM_P},
     {0x7, MemoryTechType::IntelOptane}};
 
+struct memoryLocation
+{
+    uint8_t memoryController;
+    uint8_t socket;
+    uint8_t slot;
+    uint8_t channel;
+};
+
 } // namespace smbios
 
 } // namespace phosphor
diff --git a/src/dimm.cpp b/src/dimm.cpp
index b667179..dc8bb92 100644
--- a/src/dimm.cpp
+++ b/src/dimm.cpp
@@ -21,6 +21,8 @@
 #include <boost/algorithm/string.hpp>
 #include <phosphor-logging/elog-errors.hpp>
 
+#include <fstream>
+#include <iostream>
 #include <regex>
 
 namespace phosphor
@@ -41,6 +43,10 @@
     sdbusplus::server::xyz::openbmc_project::inventory::item::Dimm::Ecc;
 
 static constexpr uint16_t maxOldDimmSize = 0x7fff;
+
+static constexpr const char* filename =
+    "/usr/share/smbios-mdr/memoryLocationTable.json";
+
 void Dimm::memoryInfoUpdate(uint8_t* smbiosTableStorage,
                             const std::string& motherboard)
 {
@@ -215,21 +221,54 @@
     const std::string substrCpu = "CPU";
     auto cpuPos = deviceLocator.find(substrCpu);
 
-    if (cpuPos != std::string::npos)
+    auto data = parseConfigFile();
+
+    if (!data.empty())
     {
-        std::string socketString =
-            deviceLocator.substr(cpuPos + substrCpu.length(), 1);
-        try
+        auto it = data.find(deviceLocator);
+
+        if (it != data.end())
         {
-            uint8_t socketNum =
-                static_cast<uint8_t>(std::stoi(socketString) + 1);
-            socket(socketNum);
+            uint8_t memoryControllerValue =
+                it.value()["MemoryController"].get<uint8_t>();
+            uint8_t socketValue = it.value()["Socket"].get<uint8_t>();
+            uint8_t slotValue = it.value()["Slot"].get<uint8_t>();
+            uint8_t channelValue = it.value()["Channel"].get<uint8_t>();
+
+            socket(memoryControllerValue);
+            memoryController(socketValue);
+            slot(slotValue);
+            channel(channelValue);
         }
-        catch (const sdbusplus::exception_t& ex)
+        else
         {
+            socket(0);
+            memoryController(0);
+            slot(0);
+            channel(0);
             phosphor::logging::log<phosphor::logging::level::ERR>(
-                "std::stoi operation failed ",
-                phosphor::logging::entry("ERROR=%s", ex.what()));
+                "Failed find the corresponding table for dimm ",
+                phosphor::logging::entry("DIMM:%s", deviceLocator.c_str()));
+        }
+    }
+    else
+    {
+        if (cpuPos != std::string::npos)
+        {
+            std::string socketString =
+                deviceLocator.substr(cpuPos + substrCpu.length(), 1);
+            try
+            {
+                uint8_t socketNum =
+                    static_cast<uint8_t>(std::stoi(socketString) + 1);
+                socket(socketNum);
+            }
+            catch (const sdbusplus::exception_t& ex)
+            {
+                phosphor::logging::log<phosphor::logging::level::ERR>(
+                    "std::stoi operation failed ",
+                    phosphor::logging::entry("ERROR=%s", ex.what()));
+            }
         }
     }
 
@@ -398,6 +437,18 @@
         MemoryLocation::slot(value);
 }
 
+uint8_t Dimm::memoryController(uint8_t value)
+{
+    return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm::
+        MemoryLocation::memoryController(value);
+}
+
+uint8_t Dimm::channel(uint8_t value)
+{
+    return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm::
+        MemoryLocation::channel(value);
+}
+
 uint8_t Dimm::socket(uint8_t value)
 {
     return sdbusplus::server::xyz::openbmc_project::inventory::item::dimm::
@@ -416,5 +467,28 @@
         OperationalStatus::functional(value);
 }
 
+Json Dimm::parseConfigFile()
+{
+    std::ifstream memoryLocationFile(filename);
+
+    if (!memoryLocationFile.is_open())
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "config JSON file not found, FILENAME ",
+            phosphor::logging::entry("%s", filename));
+        return {};
+    }
+
+    auto data = Json::parse(memoryLocationFile, nullptr, false);
+    if (data.is_discarded())
+    {
+        phosphor::logging::log<phosphor::logging::level::ERR>(
+            "config readings JSON parser failure");
+        return {};
+    }
+
+    return data;
+}
+
 } // namespace smbios
 } // namespace phosphor