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/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;
 };