#include "ddimm_parser.hpp" #include "constants.hpp" #include "exceptions.hpp" #include #include #include #include #include namespace vpd { static constexpr auto SDRAM_DENSITY_PER_DIE_24GB = 24; static constexpr auto SDRAM_DENSITY_PER_DIE_32GB = 32; static constexpr auto SDRAM_DENSITY_PER_DIE_48GB = 48; static constexpr auto SDRAM_DENSITY_PER_DIE_64GB = 64; static constexpr auto SDRAM_DENSITY_PER_DIE_UNDEFINED = 0; static constexpr auto PRIMARY_BUS_WIDTH_32_BITS = 32; static constexpr auto PRIMARY_BUS_WIDTH_UNUSED = 0; bool DdimmVpdParser::checkValidValue(uint8_t i_ByteValue, uint8_t i_shift, uint8_t i_minValue, uint8_t i_maxValue) { bool l_isValid = true; uint8_t l_ByteValue = i_ByteValue >> i_shift; if ((l_ByteValue > i_maxValue) || (l_ByteValue < i_minValue)) { logging::logMessage( "Non valid Value encountered value[" + std::to_string(l_ByteValue) + "] range [" + std::to_string(i_minValue) + ".." + std::to_string(i_maxValue) + "] found "); return false; } return l_isValid; } uint8_t DdimmVpdParser::getDdr5DensityPerDie(uint8_t i_ByteValue) { uint8_t l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED; if (i_ByteValue < constants::VALUE_5) { l_densityPerDie = i_ByteValue * constants::VALUE_4; } else { switch (i_ByteValue) { case constants::VALUE_5: l_densityPerDie = SDRAM_DENSITY_PER_DIE_24GB; break; case constants::VALUE_6: l_densityPerDie = SDRAM_DENSITY_PER_DIE_32GB; break; case constants::VALUE_7: l_densityPerDie = SDRAM_DENSITY_PER_DIE_48GB; break; case constants::VALUE_8: l_densityPerDie = SDRAM_DENSITY_PER_DIE_64GB; break; default: logging::logMessage( "default value encountered for density per die"); l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED; break; } } return l_densityPerDie; } uint8_t DdimmVpdParser::getDdr5DiePerPackage(uint8_t i_ByteValue) { uint8_t l_DiePerPackage = constants::VALUE_0; if (i_ByteValue < constants::VALUE_2) { l_DiePerPackage = i_ByteValue + constants::VALUE_1; } else { l_DiePerPackage = pow(constants::VALUE_2, (i_ByteValue - constants::VALUE_1)); } return l_DiePerPackage; } size_t DdimmVpdParser::getDdr5BasedDdimmSize( types::BinaryVector::const_iterator i_iterator) { size_t l_dimmSize = 0; do { if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BITS_01, constants::SHIFT_BITS_0, constants::VALUE_1, constants::VALUE_3) || !checkValidValue(i_iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BITS_345, constants::SHIFT_BITS_3, constants::VALUE_1, constants::VALUE_3)) { logging::logMessage( "Capacity calculation failed for channels per DIMM. DDIMM Byte " "235 value [" + std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]"); break; } uint8_t l_channelsPerPhy = (((i_iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BITS_01) ? constants::VALUE_1 : constants::VALUE_0) + ((i_iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BITS_345) ? constants::VALUE_1 : constants::VALUE_0)); uint8_t l_channelsPerDdimm = (((i_iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BIT_6) >> constants::VALUE_6) + ((i_iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BIT_7) >> constants::VALUE_7)) * l_channelsPerPhy; if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BITS_012, constants::SHIFT_BITS_0, constants::VALUE_1, constants::VALUE_3)) { logging::logMessage( "Capacity calculation failed for bus width per channel. DDIMM " "Byte 235 value [" + std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]"); break; } uint8_t l_busWidthPerChannel = (i_iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BITS_012) ? PRIMARY_BUS_WIDTH_32_BITS : PRIMARY_BUS_WIDTH_UNUSED; if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] & constants::MASK_BYTE_BITS_567, constants::SHIFT_BITS_5, constants::VALUE_0, constants::VALUE_5)) { logging::logMessage( "Capacity calculation failed for die per package. DDIMM Byte 4 " "value [" + std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]"); break; } uint8_t l_diePerPackage = getDdr5DiePerPackage( (i_iterator[constants::SPD_BYTE_4] & constants::MASK_BYTE_BITS_567) >> constants::VALUE_5); if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] & constants::MASK_BYTE_BITS_01234, constants::SHIFT_BITS_0, constants::VALUE_1, constants::VALUE_8)) { logging::logMessage( "Capacity calculation failed for SDRAM Density per Die. DDIMM " "Byte 4 value [" + std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]"); break; } uint8_t l_densityPerDie = getDdr5DensityPerDie( i_iterator[constants::SPD_BYTE_4] & constants::MASK_BYTE_BITS_01234); uint8_t l_ranksPerChannel = 0; if (((i_iterator[constants::SPD_BYTE_234] & constants::MASK_BYTE_BIT_7) >> constants::VALUE_7)) { l_ranksPerChannel = ((i_iterator[constants::SPD_BYTE_234] & constants::MASK_BYTE_BITS_345) >> constants::VALUE_3) + constants::VALUE_1; } else if (((i_iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BIT_6) >> constants::VALUE_6)) { l_ranksPerChannel = (i_iterator[constants::SPD_BYTE_234] & constants::MASK_BYTE_BITS_012) + constants::VALUE_1; } if (!checkValidValue(i_iterator[constants::SPD_BYTE_6] & constants::MASK_BYTE_BITS_567, constants::SHIFT_BITS_5, constants::VALUE_0, constants::VALUE_3)) { logging::logMessage( "Capacity calculation failed for dram width DDIMM Byte 6 value " "[" + std::to_string(i_iterator[constants::SPD_BYTE_6]) + "]"); break; } uint8_t l_dramWidth = constants::VALUE_4 * (constants::VALUE_1 << ((i_iterator[constants::SPD_BYTE_6] & constants::MASK_BYTE_BITS_567) >> constants::VALUE_5)); // DDIMM size is calculated in GB l_dimmSize = (l_channelsPerDdimm * l_busWidthPerChannel * l_diePerPackage * l_densityPerDie * l_ranksPerChannel) / (8 * l_dramWidth); } while (false); return constants::CONVERT_GB_TO_KB * l_dimmSize; } size_t DdimmVpdParser::getDdr4BasedDdimmSize( types::BinaryVector::const_iterator i_iterator) { size_t l_dimmSize = 0; try { uint8_t l_tmpValue = 0; // Calculate SDRAM capacity l_tmpValue = i_iterator[constants::SPD_BYTE_4] & constants::JEDEC_SDRAM_CAP_MASK; /* Make sure the bits are not Reserved */ if (l_tmpValue > constants::JEDEC_SDRAMCAP_RESERVED) { throw std::runtime_error( "Bad data in VPD byte 4. Can't calculate SDRAM capacity and so " "dimm size.\n "); } uint16_t l_sdramCapacity = 1; l_sdramCapacity = (l_sdramCapacity << l_tmpValue) * constants::JEDEC_SDRAMCAP_MULTIPLIER; /* Calculate Primary bus width */ l_tmpValue = i_iterator[constants::SPD_BYTE_13] & constants::JEDEC_PRI_BUS_WIDTH_MASK; if (l_tmpValue > constants::JEDEC_RESERVED_BITS) { throw std::runtime_error( "Bad data in VPD byte 13. Can't calculate primary bus width " "and so dimm size."); } uint8_t l_primaryBusWid = 1; l_primaryBusWid = (l_primaryBusWid << l_tmpValue) * constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER; /* Calculate SDRAM width */ l_tmpValue = i_iterator[constants::SPD_BYTE_12] & constants::JEDEC_SDRAM_WIDTH_MASK; if (l_tmpValue > constants::JEDEC_RESERVED_BITS) { throw std::runtime_error( "Bad data in VPD byte 12. Can't calculate SDRAM width and so " "dimm size."); } uint8_t l_sdramWidth = 1; l_sdramWidth = (l_sdramWidth << l_tmpValue) * constants::JEDEC_SDRAM_WIDTH_MULTIPLIER; /* Calculate Number of ranks */ l_tmpValue = i_iterator[constants::SPD_BYTE_12] & constants::JEDEC_NUM_RANKS_MASK; l_tmpValue >>= constants::JEDEC_RESERVED_BITS; if (l_tmpValue > constants::JEDEC_RESERVED_BITS) { throw std::runtime_error( "Bad data in VPD byte 12, can't calculate number of ranks. Invalid data found."); } uint8_t l_logicalRanksPerDimm = l_tmpValue + 1; // Determine is single load stack (3DS) or not l_tmpValue = i_iterator[constants::SPD_BYTE_6] & constants::JEDEC_SIGNAL_LOADING_MASK; if (l_tmpValue == constants::JEDEC_SINGLE_LOAD_STACK) { // Fetch die count l_tmpValue = i_iterator[constants::SPD_BYTE_6] & constants::JEDEC_DIE_COUNT_MASK; l_tmpValue >>= constants::JEDEC_DIE_COUNT_RIGHT_SHIFT; uint8_t l_dieCount = l_tmpValue + 1; l_logicalRanksPerDimm *= l_dieCount; } l_dimmSize = (l_sdramCapacity / constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER) * (l_primaryBusWid / l_sdramWidth) * l_logicalRanksPerDimm; // Converting dimm size from MB to KB l_dimmSize *= constants::CONVERT_MB_TO_KB; } catch (const std::exception& l_ex) { // TODO:: Need an error log here logging::logMessage("DDR4 DDIMM calculation is failed, reason: " + std::string(l_ex.what())); } return l_dimmSize; } size_t DdimmVpdParser::getDdimmSize(types::BinaryVector::const_iterator i_iterator) { size_t l_dimmSize = 0; if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR5) { l_dimmSize = getDdr5BasedDdimmSize(i_iterator); } else if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR4) { l_dimmSize = getDdr4BasedDdimmSize(i_iterator); } else { logging::logMessage( "Error: DDIMM is neither DDR4 nor DDR5. DDIMM Byte 2 value [" + std::to_string(i_iterator[constants::SPD_BYTE_2]) + "]"); } return l_dimmSize; } void DdimmVpdParser::readKeywords(types::BinaryVector::const_iterator i_iterator) { // collect DDIMM size value auto l_dimmSize = getDdimmSize(i_iterator); if (!l_dimmSize) { throw(DataException("Error: Calculated dimm size is 0.")); } m_parsedVpdMap.emplace("MemorySizeInKB", l_dimmSize); // point the i_iterator to DIMM data and skip "11S" advance(i_iterator, constants::DDIMM_11S_BARCODE_START + constants::DDIMM_11S_FORMAT_LEN); types::BinaryVector l_partNumber(i_iterator, i_iterator + constants::PART_NUM_LEN); advance(i_iterator, constants::PART_NUM_LEN); types::BinaryVector l_serialNumber(i_iterator, i_iterator + constants::SERIAL_NUM_LEN); advance(i_iterator, constants::SERIAL_NUM_LEN); types::BinaryVector l_ccin(i_iterator, i_iterator + constants::CCIN_LEN); m_parsedVpdMap.emplace("FN", l_partNumber); m_parsedVpdMap.emplace("PN", move(l_partNumber)); m_parsedVpdMap.emplace("SN", move(l_serialNumber)); m_parsedVpdMap.emplace("CC", move(l_ccin)); } types::VPDMapVariant DdimmVpdParser::parse() { try { // Read the data and return the map auto l_iterator = m_vpdVector.cbegin(); readKeywords(l_iterator); return m_parsedVpdMap; } catch (const std::exception& exp) { logging::logMessage(exp.what()); throw exp; } } } // namespace vpd