| #include "ddimm_parser.hpp" |
| |
| #include "constants.hpp" |
| #include "exceptions.hpp" |
| |
| #include <cmath> |
| #include <cstdint> |
| #include <iostream> |
| #include <numeric> |
| #include <string> |
| |
| 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; |
| static constexpr auto DRAM_MANUFACTURER_ID_OFFSET = 0x228; |
| static constexpr auto DRAM_MANUFACTURER_ID_LENGTH = 0x02; |
| |
| 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); |
| |
| types::BinaryVector l_mfgId(DRAM_MANUFACTURER_ID_LENGTH); |
| std::copy_n((m_vpdVector.cbegin() + DRAM_MANUFACTURER_ID_OFFSET), |
| DRAM_MANUFACTURER_ID_LENGTH, l_mfgId.begin()); |
| |
| 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)); |
| m_parsedVpdMap.emplace("DI", move(l_mfgId)); |
| } |
| |
| 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 |