Dimm memory size support

This commit will calculate the memory size for Dimm and
populate the value with property MemorySizeinKB on dbus.

Test- Tested on simics 2s2u

Set the property with dummy value-

root@rain71bmc:/tmp#  busctl introspect  xyz.openbmc_project.Inventory.Manager  /xyz/openbmc_project/inventory/system/chassis/motherboard/dimm0| grep -e MemorySizeInKB -e  Present -e PrettyName -e Model  -e PartNumber  -e SerialNumber
.Model                                                property  s         "327A"                                   emits-change writable
.PartNumber                                           property  s         "78P6574"                                emits-change writable
.SerialNumber                                         property  s         "YH301T01P00G"                           emits-change writable
.SparePartNumber                                      property  s         ""                                       emits-change writable
.SubModel                                             property  s         ""                                       emits-change writable
.Present                                              property  b         true                                     emits-change writable
.PrettyName                                           property  s         "Memory DIMM"                            emits-change writable
.MemorySizeInKB                                       property  u         33554432                                 emits-change writable
root@rain71bmc:/tmp#

Signed-off-by: Alpana Kumari <alpankum@in.ibm.com>
Change-Id: I70e5bc3f538379f9891ae0ede6cdf09a585520a4
diff --git a/const.hpp b/const.hpp
index e173e89..5cab687 100644
--- a/const.hpp
+++ b/const.hpp
@@ -68,6 +68,7 @@
 static constexpr auto PART_NUM_LEN = 7;
 static constexpr auto SERIAL_NUM_LEN = 12;
 static constexpr auto CCIN_LEN = 4;
+static constexpr auto CONVERT_MB_TO_KB = 1024;
 
 using namespace std::string_literals;
 constexpr auto pimPath = "/xyz/openbmc_project/inventory";
diff --git a/ibm_vpd_app.cpp b/ibm_vpd_app.cpp
index 684b7a7..bb0088f 100644
--- a/ibm_vpd_app.cpp
+++ b/ibm_vpd_app.cpp
@@ -159,8 +159,6 @@
 
     for (const auto& kwVal : map)
     {
-        vector<uint8_t> vec(kwVal.second.begin(), kwVal.second.end());
-
         auto kw = kwVal.first;
 
         if (kw[0] == '#')
@@ -171,7 +169,41 @@
         {
             kw = string("N_") + kw;
         }
-        prop.emplace(move(kw), move(vec));
+        if constexpr (is_same<T, KeywordVpdMap>::value)
+        {
+            if (get_if<Binary>(&kwVal.second))
+            {
+                Binary vec(get_if<Binary>(&kwVal.second)->begin(),
+                           get_if<Binary>(&kwVal.second)->end());
+
+                prop.emplace(move(kw), move(vec));
+            }
+            else
+            {
+                if (kw == "MemorySizeInKB")
+                {
+                    inventory::PropertyMap memProp;
+                    auto memVal = get_if<size_t>(&kwVal.second);
+                    if (memVal)
+                    {
+                        memProp.emplace(move(kw),
+                                        ((*memVal) * CONVERT_MB_TO_KB));
+                        interfaces.emplace(
+                            "xyz.openbmc_project.Inventory.Item.Dimm",
+                            move(memProp));
+                    }
+                    else
+                    {
+                        cerr << "MemorySizeInKB value not found in vpd map\n";
+                    }
+                }
+            }
+        }
+        else
+        {
+            Binary vec(kwVal.second.begin(), kwVal.second.end());
+            prop.emplace(move(kw), move(vec));
+        }
     }
 
     interfaces.emplace(preIntrStr, move(prop));
@@ -259,10 +291,22 @@
                 {
                     if (!kw.empty() && vpdMap.count(kw))
                     {
-                        auto prop =
-                            string(vpdMap.at(kw).begin(), vpdMap.at(kw).end());
-                        auto encoded = encodeKeyword(prop, encoding);
-                        props.emplace(busProp, encoded);
+                        auto kwValue = get_if<Binary>(&vpdMap.at(kw));
+                        auto uintValue = get_if<size_t>(&vpdMap.at(kw));
+
+                        if (kwValue)
+                        {
+                            auto prop =
+                                string((*kwValue).begin(), (*kwValue).end());
+
+                            auto encoded = encodeKeyword(prop, encoding);
+
+                            props.emplace(busProp, encoded);
+                        }
+                        else if (uintValue)
+                        {
+                            props.emplace(busProp, *uintValue);
+                        }
                     }
                 }
             }
@@ -1069,4 +1113,4 @@
     }
 
     return rc;
-}
+}
\ No newline at end of file
diff --git a/test/keyword_vpd_parser_test/kw_vpd_test.cpp b/test/keyword_vpd_parser_test/kw_vpd_test.cpp
index 48c3961..413e31b 100644
--- a/test/keyword_vpd_parser_test/kw_vpd_test.cpp
+++ b/test/keyword_vpd_parser_test/kw_vpd_test.cpp
@@ -38,50 +38,59 @@
 TEST_F(KeywordVpdParserTest, GoodTestCase)
 {
     KeywordVpdParser parserObj1(std::move(keywordVpdVector));
-    KeywordVpdMap map1 = {
-        {"WI", {0x00}},
-        {"FL", {0x50, 0x32, 0x20, 0x20, 0x20}},
-        {"SM",
-         {0x82, 0x50, 0x32, 0x2d, 0x44, 0x34, 0x20, 0x20, 0x20, 0x20, 0x20,
-          0x20, 0x32, 0x53, 0x53, 0x43, 0x81, 0x50, 0x32, 0x2d, 0x44, 0x35,
-          0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x32, 0x53, 0x53, 0x43, 0x80,
-          0x50, 0x32, 0x2d, 0x44, 0x37, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
-          0x32, 0x53, 0x53, 0x43, 0x83, 0x50, 0x32, 0x2d, 0x44, 0x38, 0x20,
-          0x20, 0x20, 0x20, 0x20, 0x20, 0x32, 0x53, 0x53, 0x43}},
-        {"B2",
-         {0x50, 0x05, 0x07, 0x60, 0x73, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00,
-          0x00, 0x00, 0x00, 0x01, 0x00}},
-        {"MF", {0x00, 0x10}},
-        {"VZ", {0x30, 0x33}},
-        {"PN", {0x30, 0x31, 0x4b, 0x55, 0x37, 0x32, 0x34}},
-        {"FN", {0x20, 0x30, 0x31, 0x4b, 0x55, 0x37, 0x32, 0x34}},
-        {"CE", {0x31}},
-        {"SN",
-         {0x59, 0x48, 0x33, 0x30, 0x42, 0x47, 0x37, 0x38, 0x42, 0x30, 0x31,
-          0x34}},
-        {"CC", {0x32, 0x44, 0x33, 0x37}}};
+    KeywordVpdMap map1{
+        pair<std::string, Binary>{"WI", {0x00}},
+        pair<std::string, Binary>{"FL", {0x50, 0x32, 0x20, 0x20, 0x20}},
+        pair<std::string, Binary>{
+            "SM",
+            {0x82, 0x50, 0x32, 0x2d, 0x44, 0x34, 0x20, 0x20, 0x20, 0x20, 0x20,
+             0x20, 0x32, 0x53, 0x53, 0x43, 0x81, 0x50, 0x32, 0x2d, 0x44, 0x35,
+             0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x32, 0x53, 0x53, 0x43, 0x80,
+             0x50, 0x32, 0x2d, 0x44, 0x37, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+             0x32, 0x53, 0x53, 0x43, 0x83, 0x50, 0x32, 0x2d, 0x44, 0x38, 0x20,
+             0x20, 0x20, 0x20, 0x20, 0x20, 0x32, 0x53, 0x53, 0x43}},
+        pair<std::string, Binary>{"B2",
+                                  {0x50, 0x05, 0x07, 0x60, 0x73, 0x00, 0x72,
+                                   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                   0x01, 0x00}},
+        pair<std::string, Binary>{"MF", {0x00, 0x10}},
+        pair<std::string, Binary>{"VZ", {0x30, 0x33}},
+        pair<std::string, Binary>{"PN",
+                                  {0x30, 0x31, 0x4b, 0x55, 0x37, 0x32, 0x34}},
+        pair<std::string, Binary>{
+            "FN", {0x20, 0x30, 0x31, 0x4b, 0x55, 0x37, 0x32, 0x34}},
+        pair<std::string, Binary>{"CE", {0x31}},
+        pair<std::string, Binary>{"SN",
+                                  {0x59, 0x48, 0x33, 0x30, 0x42, 0x47, 0x37,
+                                   0x38, 0x42, 0x30, 0x31, 0x34}},
+        pair<std::string, Binary>{"CC", {0x32, 0x44, 0x33, 0x37}}};
 
     auto map2 = std::move(get<KeywordVpdMap>(parserObj1.parse()));
     ASSERT_EQ(1, map1 == map2);
 
     // BONO TYPE VPD
     KeywordVpdParser parserObj2(std::move(bonoKwVpdVector));
-    map1 = {{"B2",
-             {0x50, 0x0, 0xb3, 0xe0, 0x90, 0x0, 0x2, 0x50, 0x0, 0x0, 0x0, 0x0,
-              0x0, 0x0, 0x0, 0x0}},
-            {"CC", {0x35, 0x39, 0x33, 0x42}},
-            {"CT", {0x50, 0x37, 0x32, 0x0}},
-            {"EC", {0x50, 0x34, 0x35, 0x35, 0x33, 0x37}},
-            {"FN", {0x30, 0x32, 0x44, 0x45, 0x33, 0x36, 0x35}},
-            {"PN", {0x30, 0x32, 0x44, 0x45, 0x33, 0x36, 0x36}},
-            {"RV", {0xa1}},
-            {"SI", {0x31, 0x30, 0x31, 0x34, 0x30, 0x36, 0x37, 0x34}},
-            {"SN",
-             {0x59, 0x4c, 0x35, 0x30, 0x48, 0x54, 0x39, 0x36, 0x4a, 0x30, 0x30,
-              0x38}},
-            {"Z4", {0x30}},
-            {"Z5", {0x30}},
-            {"Z6", {0x41, 0x31, 0x38, 0x30, 0x30, 0x32, 0x30, 0x30}}};
+    map1 = {
+        pair<std::string, Binary>{"B2",
+                                  {0x50, 0x0, 0xb3, 0xe0, 0x90, 0x0, 0x2, 0x50,
+                                   0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}},
+        pair<std::string, Binary>{"CC", {0x35, 0x39, 0x33, 0x42}},
+        pair<std::string, Binary>{"CT", {0x50, 0x37, 0x32, 0x0}},
+        pair<std::string, Binary>{"EC", {0x50, 0x34, 0x35, 0x35, 0x33, 0x37}},
+        pair<std::string, Binary>{"FN",
+                                  {0x30, 0x32, 0x44, 0x45, 0x33, 0x36, 0x35}},
+        pair<std::string, Binary>{"PN",
+                                  {0x30, 0x32, 0x44, 0x45, 0x33, 0x36, 0x36}},
+        pair<std::string, Binary>{"RV", {0xa1}},
+        pair<std::string, Binary>{
+            "SI", {0x31, 0x30, 0x31, 0x34, 0x30, 0x36, 0x37, 0x34}},
+        pair<std::string, Binary>{"SN",
+                                  {0x59, 0x4c, 0x35, 0x30, 0x48, 0x54, 0x39,
+                                   0x36, 0x4a, 0x30, 0x30, 0x38}},
+        pair<std::string, Binary>{"Z4", {0x30}},
+        pair<std::string, Binary>{"Z5", {0x30}},
+        pair<std::string, Binary>{
+            "Z6", {0x41, 0x31, 0x38, 0x30, 0x30, 0x32, 0x30, 0x30}}};
 
     map2 = std::move(get<KeywordVpdMap>(parserObj2.parse()));
     ASSERT_EQ(1, map1 == map2);
@@ -201,4 +210,4 @@
     ::testing::InitGoogleTest(&argc, argv);
 
     return RUN_ALL_TESTS();
-}
+}
\ No newline at end of file
diff --git a/types.hpp b/types.hpp
index 8c5c6e1..080a976 100644
--- a/types.hpp
+++ b/types.hpp
@@ -23,8 +23,9 @@
 
 using Path = std::string;
 using Property = std::string;
-using Value = std::variant<bool, int64_t, std::string, Binary>;
+using Value = std::variant<bool, size_t, int64_t, std::string, Binary>;
 using PropertyMap = std::map<Property, Value>;
+using kwdVpdValueTypes = std::variant<size_t, Binary>;
 
 using Interface = std::string;
 using InterfaceMap = std::map<Interface, PropertyMap>;
@@ -40,7 +41,7 @@
 using ListOfPaths = std::vector<sdbusplus::message::object_path>;
 using NodeNumber = uint16_t;
 using namespace std::string_literals;
-using KeywordVpdMap = std::unordered_map<std::string, Binary>;
+using KeywordVpdMap = std::unordered_map<std::string, kwdVpdValueTypes>;
 
 using systemType = std::string;
 using deviceTree = std::string;
@@ -58,4 +59,4 @@
 } // namespace inventory
 
 } // namespace vpd
-} // namespace openpower
+} // namespace openpower
\ No newline at end of file
diff --git a/vpd-parser/memory_vpd_parser.cpp b/vpd-parser/memory_vpd_parser.cpp
index 3793ae0..9551e33 100644
--- a/vpd-parser/memory_vpd_parser.cpp
+++ b/vpd-parser/memory_vpd_parser.cpp
@@ -17,10 +17,107 @@
 using namespace std;
 using namespace openpower::vpd::parser;
 
+static constexpr auto MEM_BYTE_4 = 4;
+static constexpr auto MEM_BYTE_6 = 6;
+static constexpr auto MEM_BYTE_12 = 12;
+static constexpr auto MEM_BYTE_13 = 13;
+static constexpr auto JEDEC_SDRAM_CAP_MASK = 0x0F;
+static constexpr auto JEDEC_PRI_BUS_WIDTH_MASK = 0x07;
+static constexpr auto JEDEC_SDRAM_WIDTH_MASK = 0x07;
+static constexpr auto JEDEC_NUM_RANKS_MASK = 0x38;
+static constexpr auto JEDEC_DIE_COUNT_MASK = 0x70;
+static constexpr auto JEDEC_SINGLE_LOAD_STACK = 0x02;
+static constexpr auto JEDEC_SIGNAL_LOADING_MASK = 0x03;
+
+static constexpr auto JEDEC_SDRAMCAP_MULTIPLIER = 256;
+static constexpr auto JEDEC_PRI_BUS_WIDTH_MULTIPLIER = 8;
+static constexpr auto JEDEC_SDRAM_WIDTH_MULTIPLIER = 4;
+static constexpr auto JEDEC_SDRAMCAP_RESERVED = 6;
+static constexpr auto JEDEC_RESERVED_BITS = 3;
+static constexpr auto JEDEC_DIE_COUNT_RIGHT_SHIFT = 4;
+
+auto memoryVpdParser::getDimmSize(Binary::const_iterator iterator)
+{
+    size_t tmp = 0, dimmSize = 0;
+
+    size_t sdramCap = 1, priBusWid = 1, sdramWid = 1, logicalRanksPerDimm = 1;
+    Byte dieCount = 1;
+
+    // NOTE: This calculation is Only for DDR4
+
+    // Calculate SDRAM  capacity
+    tmp = iterator[MEM_BYTE_4] & JEDEC_SDRAM_CAP_MASK;
+    /* Make sure the bits are not Reserved */
+    if (tmp > JEDEC_SDRAMCAP_RESERVED)
+    {
+        cerr << "Bad data in vpd byte 4. Can't calculate SDRAM capacity and so "
+                "dimm size.\n ";
+        return dimmSize;
+    }
+
+    sdramCap = (sdramCap << tmp) * JEDEC_SDRAMCAP_MULTIPLIER;
+
+    /* Calculate Primary bus width */
+    tmp = iterator[MEM_BYTE_13] & JEDEC_PRI_BUS_WIDTH_MASK;
+    if (tmp > JEDEC_RESERVED_BITS)
+    {
+        cerr << "Bad data in vpd byte 13. Can't calculate primary bus width "
+                "and so dimm size.\n ";
+        return dimmSize;
+    }
+    priBusWid = (priBusWid << tmp) * JEDEC_PRI_BUS_WIDTH_MULTIPLIER;
+
+    /* Calculate SDRAM width */
+    tmp = iterator[MEM_BYTE_12] & JEDEC_SDRAM_WIDTH_MASK;
+    if (tmp > JEDEC_RESERVED_BITS)
+    {
+        cerr << "Bad data in vpd byte 12. Can't calculate SDRAM width and so "
+                "dimm size.\n ";
+        return dimmSize;
+    }
+    sdramWid = (sdramWid << tmp) * JEDEC_SDRAM_WIDTH_MULTIPLIER;
+
+    tmp = iterator[MEM_BYTE_6] & JEDEC_SIGNAL_LOADING_MASK;
+
+    if (tmp == JEDEC_SINGLE_LOAD_STACK)
+    {
+        // Fetch die count
+        tmp = iterator[MEM_BYTE_6] & JEDEC_DIE_COUNT_MASK;
+        tmp >>= JEDEC_DIE_COUNT_RIGHT_SHIFT;
+        dieCount = tmp + 1;
+    }
+
+    /* Calculate Number of ranks */
+    tmp = iterator[MEM_BYTE_12] & JEDEC_NUM_RANKS_MASK;
+    tmp >>= JEDEC_RESERVED_BITS;
+
+    if (tmp > JEDEC_RESERVED_BITS)
+    {
+        cerr << "Can't calculate number of ranks. Invalid data found.\n ";
+        return dimmSize;
+    }
+    logicalRanksPerDimm = (tmp + 1) * dieCount;
+
+    dimmSize = (sdramCap / JEDEC_PRI_BUS_WIDTH_MULTIPLIER) *
+               (priBusWid / sdramWid) * logicalRanksPerDimm;
+
+    return dimmSize;
+}
+
 kwdVpdMap memoryVpdParser::readKeywords(Binary::const_iterator iterator)
 {
     KeywordVpdMap map{};
 
+    // collect Dimm size value
+    auto dimmSize = getDimmSize(iterator);
+    if (!dimmSize)
+    {
+        cerr << "Error: Calculated dimm size is 0.";
+    }
+
+    map.emplace("MemorySizeInKB", dimmSize);
+    // point the iterator to DIMM data and skip "11S"
+    advance(iterator, MEMORY_VPD_DATA_START + 3);
     Binary partNumber(iterator, iterator + PART_NUM_LEN);
 
     advance(iterator, PART_NUM_LEN);
@@ -40,9 +137,6 @@
 {
     // Read the data and return the map
     auto iterator = memVpd.cbegin();
-    // point the iterator to DIMM data and skip "11S"
-    advance(iterator, MEMORY_VPD_DATA_START + 3);
-
     auto vpdDataMap = readKeywords(iterator);
 
     return vpdDataMap;
diff --git a/vpd-parser/memory_vpd_parser.hpp b/vpd-parser/memory_vpd_parser.hpp
index 27efafe..cbd44bb 100644
--- a/vpd-parser/memory_vpd_parser.hpp
+++ b/vpd-parser/memory_vpd_parser.hpp
@@ -58,6 +58,14 @@
      */
     kwdVpdMap readKeywords(Binary::const_iterator iterator);
 
+    /**
+     * @brief This function calculates dimm size from DIMM VPD
+     *
+     * @param[in] iterator - iterator to buffer containing VPD
+     * @return calculated data or 0 in case of any error.
+     */
+    auto getDimmSize(Binary::const_iterator iterator);
+
     // vdp file to be parsed
     const Binary& memVpd;
 };