blob: 589dd684a1f911434265b05e4bf0a0a52b594866 [file] [log] [blame] [edit]
#include "isdimm_vpd_parser.hpp"
#include <iostream>
#include <numeric>
#include <string>
namespace openpower
{
namespace vpd
{
namespace isdimm
{
namespace parser
{
static constexpr auto SPD_JEDEC_DDR4_SDRAM_CAP_MASK = 0x0F;
static constexpr auto SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MASK = 0x07;
static constexpr auto SPD_JEDEC_DDR4_SDRAM_WIDTH_MASK = 0x07;
static constexpr auto SPD_JEDEC_DDR4_NUM_RANKS_MASK = 0x38;
static constexpr auto SPD_JEDEC_DDR4_DIE_COUNT_MASK = 0x70;
static constexpr auto SPD_JEDEC_DDR4_SINGLE_LOAD_STACK = 0x02;
static constexpr auto SPD_JEDEC_DDR4_SIGNAL_LOADING_MASK = 0x03;
static constexpr auto SPD_JEDEC_DDR4_SDRAMCAP_MULTIPLIER = 256;
static constexpr auto SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER = 8;
static constexpr auto SPD_JEDEC_DDR4_SDRAM_WIDTH_MULTIPLIER = 4;
static constexpr auto SPD_JEDEC_DDR4_SDRAMCAP_RESERVED = 8;
static constexpr auto SPD_JEDEC_DDR4_4_RESERVED_BITS = 4;
static constexpr auto SPD_JEDEC_DDR4_3_RESERVED_BITS = 3;
static constexpr auto SPD_JEDEC_DDR4_DIE_COUNT_RIGHT_SHIFT = 4;
static constexpr auto SPD_JEDEC_DDR4_MFG_ID_MSB_OFFSET = 321;
static constexpr auto SPD_JEDEC_DDR4_MFG_ID_LSB_OFFSET = 320;
static constexpr auto SPD_JEDEC_DDR4_SN_BYTE0_OFFSET = 325;
static constexpr auto SPD_JEDEC_DDR4_SN_BYTE1_OFFSET = 326;
static constexpr auto SPD_JEDEC_DDR4_SN_BYTE2_OFFSET = 327;
static constexpr auto SPD_JEDEC_DDR4_SN_BYTE3_OFFSET = 328;
static constexpr auto SPD_JEDEC_DDR4_SDRAM_DENSITY_BANK_OFFSET = 4;
static constexpr auto SPD_JEDEC_DDR4_SDRAM_ADDR_OFFSET = 5;
static constexpr auto SPD_JEDEC_DDR4_DRAM_PRI_PACKAGE_OFFSET = 6;
static constexpr auto SPD_JEDEC_DDR4_DRAM_MODULE_ORG_OFFSET = 12;
static constexpr auto SPD_JEDEC_DDR4_DRAM_MANUFACTURER_ID_OFFSET = 320;
static constexpr auto SPD_JEDEC_DRAM_MANUFACTURER_ID_LENGTH = 2;
// DDR5 JEDEC specification constants
static constexpr auto SPD_JEDEC_DDR5_SUB_CHANNELS_PER_DIMM = 235;
static constexpr auto SPD_JEDEC_DDR5_SUB_CHANNELS_PER_DIMM_MASK = 0x60;
static constexpr auto SPD_JEDEC_DDR5_PRI_BUS_WIDTH_PER_CHANNEL = 235;
static constexpr auto SPD_JEDEC_DDR5_PRI_BUS_WIDTH_PER_CHANNEL_MASK = 0x07;
static constexpr auto SPD_JEDEC_DDR5_SDRAM_IO_WIDTH_SYM_ALL = 6;
static constexpr auto SPD_JEDEC_DDR5_SDRAM_IO_WIDTH_ASYM_EVEN = 6;
static constexpr auto SPD_JEDEC_DDR5_SDRAM_IO_WIDTH_ASYM_ODD = 10;
static constexpr auto SPD_JEDEC_DDR5_SDRAM_IO_WIDTH_MASK = 0xE0;
static constexpr auto SPD_JEDEC_DDR5_DIE_PER_PKG_SYM_ALL = 4;
static constexpr auto SPD_JEDEC_DDR5_DIE_PER_PKG_ASYM_EVEN = 4;
static constexpr auto SPD_JEDEC_DDR5_DIE_PER_PKG_ASYM_ODD = 8;
static constexpr auto SPD_JEDEC_DDR5_DIE_PER_PKG_MASK = 0xE0;
static constexpr auto SPD_JEDEC_DDR5_SDRAM_DENSITY_PER_DIE_SYM_ALL = 4;
static constexpr auto SPD_JEDEC_DDR5_SDRAM_DENSITY_PER_DIE_ASYM_EVEN = 4;
static constexpr auto SPD_JEDEC_DDR5_SDRAM_DENSITY_PER_DIE_ASYM_ODD = 8;
static constexpr auto SPD_JEDEC_DDR5_SDRAM_DENSITY_PER_DIE_MASK = 0x1F;
static constexpr auto SPD_JEDEC_DDR5_RANK_MIX = 234;
static constexpr auto SPD_JEDEC_DDR5_RANK_MIX_SYMMETRICAL_MASK = 0x40;
static constexpr auto SPD_JEDEC_DDR5_DRAM_MANUFACTURER_ID_OFFSET = 552;
auto isdimmVpdParser::getDDR4DimmCapacity(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[constants::SPD_BYTE_4] & SPD_JEDEC_DDR4_SDRAM_CAP_MASK;
/* Make sure the bits are not Reserved */
if (tmp >= SPD_JEDEC_DDR4_SDRAMCAP_RESERVED)
{
std::cerr
<< "Bad data in spd byte 4. Can't calculate SDRAM capacity and so "
"dimm size.\n ";
return dimmSize;
}
sdramCap = (sdramCap << tmp) * SPD_JEDEC_DDR4_SDRAMCAP_MULTIPLIER;
/* Calculate Primary bus width */
tmp = iterator[constants::SPD_BYTE_13] & SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MASK;
if (tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
{
std::cerr
<< "Bad data in spd byte 13. Can't calculate primary bus width "
"and so dimm size.\n ";
return dimmSize;
}
priBusWid = (priBusWid << tmp) * SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER;
/* Calculate SDRAM width */
tmp = iterator[constants::SPD_BYTE_12] & SPD_JEDEC_DDR4_SDRAM_WIDTH_MASK;
if (tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
{
std::cerr
<< "Bad data in vpd byte 12. Can't calculate SDRAM width and so "
"dimm size.\n ";
return dimmSize;
}
sdramWid = (sdramWid << tmp) * SPD_JEDEC_DDR4_SDRAM_WIDTH_MULTIPLIER;
tmp = iterator[constants::SPD_BYTE_6] & SPD_JEDEC_DDR4_SIGNAL_LOADING_MASK;
if (tmp == SPD_JEDEC_DDR4_SINGLE_LOAD_STACK)
{
// Fetch die count
tmp = iterator[constants::SPD_BYTE_6] & SPD_JEDEC_DDR4_DIE_COUNT_MASK;
tmp >>= SPD_JEDEC_DDR4_DIE_COUNT_RIGHT_SHIFT;
dieCount = tmp + 1;
}
/* Calculate Number of ranks */
tmp = iterator[constants::SPD_BYTE_12] & SPD_JEDEC_DDR4_NUM_RANKS_MASK;
tmp >>= SPD_JEDEC_DDR4_3_RESERVED_BITS;
if (tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
{
std::cerr << "Can't calculate number of ranks. Invalid data found.\n ";
return dimmSize;
}
logicalRanksPerDimm = (tmp + 1) * dieCount;
dimmSize = (sdramCap / SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER) *
(priBusWid / sdramWid) * logicalRanksPerDimm;
return dimmSize;
}
auto isdimmVpdParser::getDDR4PartNumber(Binary::const_iterator& iterator)
{
char tmpPN[constants::PART_NUM_LEN + 1] = {'\0'};
sprintf(tmpPN, "%02X%02X%02X%X",
iterator[SPD_JEDEC_DDR4_SDRAM_DENSITY_BANK_OFFSET],
iterator[SPD_JEDEC_DDR4_SDRAM_ADDR_OFFSET],
iterator[SPD_JEDEC_DDR4_DRAM_PRI_PACKAGE_OFFSET],
iterator[SPD_JEDEC_DDR4_DRAM_MODULE_ORG_OFFSET] & 0x0F);
std::string partNumber(tmpPN, sizeof(tmpPN) - 1);
return partNumber;
}
auto isdimmVpdParser::getDDR4SerialNumber(Binary::const_iterator& iterator)
{
char tmpSN[constants::SERIAL_NUM_LEN + 1] = {'\0'};
sprintf(tmpSN, "%02X%02X%02X%02X%02X%02X",
iterator[SPD_JEDEC_DDR4_MFG_ID_MSB_OFFSET],
iterator[SPD_JEDEC_DDR4_MFG_ID_LSB_OFFSET],
iterator[SPD_JEDEC_DDR4_SN_BYTE0_OFFSET],
iterator[SPD_JEDEC_DDR4_SN_BYTE1_OFFSET],
iterator[SPD_JEDEC_DDR4_SN_BYTE2_OFFSET],
iterator[SPD_JEDEC_DDR4_SN_BYTE3_OFFSET]);
std::string serialNumber(tmpSN, sizeof(tmpSN) - 1);
return serialNumber;
}
auto isdimmVpdParser::getDDR4FruNumber(const std::string& partNumber,
Binary::const_iterator& iterator)
{
// check for 128GB ISRDIMM not implemented
//(128GB 2RX4(8GX72) IS RDIMM 36*(16GBIT, 2H),1.2V 288PIN,1.2" ROHS) - NA
// MTB Units is used in deciding the frequency of the DIMM
// This is applicable only for DDR4 specification
// 10 - DDR4-1600
// 9 - DDR4-1866
// 8 - DDR4-2133
// 7 - DDR4-2400
// 6 - DDR4-2666
// 5 - DDR4-3200
// pnFreqFnMap < tuple <partNumber, MTBUnits>, fruNumber>
static std::map<std::tuple<std::string, uint8_t>, std::string> pnFreqFnMap =
{{std::make_tuple("8421000", 6), "78P4191"},
{std::make_tuple("8421008", 6), "78P4192"},
{std::make_tuple("8529000", 6), "78P4197"},
{std::make_tuple("8529008", 6), "78P4198"},
{std::make_tuple("8529928", 6), "78P4199"},
{std::make_tuple("8529B28", 6), "78P4200"},
{std::make_tuple("8631928", 6), "78P6925"},
{std::make_tuple("8529000", 5), "78P7317"},
{std::make_tuple("8529008", 5), "78P7318"},
{std::make_tuple("8631008", 5), "78P6815"}};
std::string fruNumber;
uint8_t mtbUnits = iterator[openpower::vpd::constants::SPD_BYTE_18] &
openpower::vpd::constants::SPD_BYTE_MASK;
std::tuple<std::string, uint8_t> tup_key = {partNumber, mtbUnits};
auto itr = pnFreqFnMap.find(tup_key);
if (itr != pnFreqFnMap.end())
{
fruNumber = itr->second;
}
else
{
fruNumber = "FFFFFFF";
}
return fruNumber;
}
auto isdimmVpdParser::getDDR4CCIN(const std::string& fruNumber)
{
static std::unordered_map<std::string, std::string> pnCCINMap = {
{"78P4191", "324D"}, {"78P4192", "324E"}, {"78P4197", "324E"},
{"78P4198", "324F"}, {"78P4199", "325A"}, {"78P4200", "324C"},
{"78P6925", "32BC"}, {"78P7317", "331A"}, {"78P7318", "331F"},
{"78P6815", "32BB"}};
std::string ccin;
auto itr = pnCCINMap.find(fruNumber);
if (itr != pnCCINMap.end())
{
ccin = itr->second;
}
else
{
ccin = "XXXX";
}
return ccin;
}
auto isdimmVpdParser::getDDR4ManufacturerId()
{
Binary mfgId(SPD_JEDEC_DRAM_MANUFACTURER_ID_LENGTH);
if (memVpd.size() < (SPD_JEDEC_DDR4_DRAM_MANUFACTURER_ID_OFFSET +
SPD_JEDEC_DRAM_MANUFACTURER_ID_LENGTH))
{
std::cout
<< "VPD length is less than the offset of Manufacturer ID. Can't fetch it"
<< std::endl;
return mfgId;
}
std::copy_n((memVpd.cbegin() + SPD_JEDEC_DDR4_DRAM_MANUFACTURER_ID_OFFSET),
SPD_JEDEC_DRAM_MANUFACTURER_ID_LENGTH, mfgId.begin());
return mfgId;
}
auto isdimmVpdParser::getDDR5DimmCapacity(Binary::const_iterator& iterator)
{
// dummy implementation to be updated when required
size_t dimmSize = 0;
(void)iterator;
return dimmSize;
}
auto isdimmVpdParser::getDDR5PartNumber(Binary::const_iterator& iterator)
{
// dummy implementation to be updated when required
std::string partNumber;
(void)iterator;
partNumber = "0123456";
return partNumber;
}
auto isdimmVpdParser::getDDR5SerialNumber(Binary::const_iterator& iterator)
{
// dummy implementation to be updated when required
std::string serialNumber;
(void)iterator;
serialNumber = "444444444444";
return serialNumber;
}
auto isdimmVpdParser::getDDR5FruNumber(const std::string& partNumber)
{
// dummy implementation to be updated when required
static std::unordered_map<std::string, std::string> pnFruMap = {
{"1234567", "XXXXXXX"}};
std::string fruNumber;
auto itr = pnFruMap.find(partNumber);
if (itr != pnFruMap.end())
{
fruNumber = itr->second;
}
else
{
fruNumber = "FFFFFFF";
}
return fruNumber;
}
auto isdimmVpdParser::getDDR5CCIN(const std::string& partNumber)
{
// dummy implementation to be updated when required
static std::unordered_map<std::string, std::string> pnCCINMap = {
{"1234567", "XXXX"}};
std::string ccin;
auto itr = pnCCINMap.find(partNumber);
if (itr != pnCCINMap.end())
{
ccin = itr->second;
}
else
{
ccin = "XXXX";
}
return ccin;
}
auto isdimmVpdParser::getDDR5ManufacturerId()
{
Binary mfgId(SPD_JEDEC_DRAM_MANUFACTURER_ID_LENGTH);
if (memVpd.size() < (SPD_JEDEC_DDR5_DRAM_MANUFACTURER_ID_OFFSET +
SPD_JEDEC_DRAM_MANUFACTURER_ID_LENGTH))
{
std::cout
<< "VPD length is less than the offset of Manufacturer ID. Can't fetch it"
<< std::endl;
return mfgId;
}
std::copy_n((memVpd.cbegin() + SPD_JEDEC_DDR5_DRAM_MANUFACTURER_ID_OFFSET),
SPD_JEDEC_DRAM_MANUFACTURER_ID_LENGTH, mfgId.begin());
return mfgId;
}
kwdVpdMap isdimmVpdParser::readKeywords(Binary::const_iterator& iterator)
{
inventory::KeywordVpdMap keywordValueMap{};
if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
constants::SPD_DRAM_TYPE_DDR5)
{
size_t dimmSize = getDDR5DimmCapacity(iterator);
if (!dimmSize)
{
std::cerr << "Error: Calculated dimm size is 0.";
}
else
{
keywordValueMap.emplace("MemorySizeInKB", dimmSize);
}
auto partNumber = getDDR5PartNumber(iterator);
auto fruNumber = getDDR5FruNumber(partNumber);
keywordValueMap.emplace("FN", move(fruNumber));
auto serialNumber = getDDR5SerialNumber(iterator);
keywordValueMap.emplace("SN", move(serialNumber));
auto ccin = getDDR5CCIN(partNumber);
keywordValueMap.emplace("CC", move(ccin));
keywordValueMap.emplace("PN", move(partNumber));
auto mfgID = getDDR5ManufacturerId();
keywordValueMap.emplace("DI", move(mfgID));
}
else if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
constants::SPD_DRAM_TYPE_DDR4)
{
size_t dimmSize = getDDR4DimmCapacity(iterator);
if (!dimmSize)
{
std::cerr << "Error: Calculated dimm size is 0.";
}
else
{
keywordValueMap.emplace("MemorySizeInKB",
(dimmSize * constants::CONVERT_MB_TO_KB));
}
auto partNumber = getDDR4PartNumber(iterator);
auto fruNumber = getDDR4FruNumber(partNumber, iterator);
auto serialNumber = getDDR4SerialNumber(iterator);
auto ccin = getDDR4CCIN(fruNumber);
auto mfgID = getDDR4ManufacturerId();
// PN value is made same as FN value
auto displayPartNumber = fruNumber;
keywordValueMap.emplace("PN", move(displayPartNumber));
keywordValueMap.emplace("FN", move(fruNumber));
keywordValueMap.emplace("SN", move(serialNumber));
keywordValueMap.emplace("CC", move(ccin));
keywordValueMap.emplace("DI", move(mfgID));
}
return keywordValueMap;
}
std::variant<kwdVpdMap, Store> isdimmVpdParser::parse()
{
// Read the data and return the map
auto iterator = memVpd.cbegin();
auto vpdDataMap = readKeywords(iterator);
return vpdDataMap;
}
} // namespace parser
} // namespace isdimm
} // namespace vpd
} // namespace openpower