1 #include "ddimm_parser.hpp" 2 3 #include "constants.hpp" 4 #include "exceptions.hpp" 5 6 #include <cmath> 7 #include <cstdint> 8 #include <iostream> 9 #include <numeric> 10 #include <string> 11 12 namespace vpd 13 { 14 15 static constexpr auto SDRAM_DENSITY_PER_DIE_24GB = 24; 16 static constexpr auto SDRAM_DENSITY_PER_DIE_32GB = 32; 17 static constexpr auto SDRAM_DENSITY_PER_DIE_48GB = 48; 18 static constexpr auto SDRAM_DENSITY_PER_DIE_64GB = 64; 19 static constexpr auto SDRAM_DENSITY_PER_DIE_UNDEFINED = 0; 20 21 static constexpr auto PRIMARY_BUS_WIDTH_32_BITS = 32; 22 static constexpr auto PRIMARY_BUS_WIDTH_UNUSED = 0; 23 static constexpr auto DRAM_MANUFACTURER_ID_OFFSET = 0x228; 24 static constexpr auto DRAM_MANUFACTURER_ID_LENGTH = 0x02; 25 26 bool DdimmVpdParser::checkValidValue(uint8_t i_ByteValue, uint8_t i_shift, 27 uint8_t i_minValue, uint8_t i_maxValue) 28 { 29 bool l_isValid = true; 30 uint8_t l_ByteValue = i_ByteValue >> i_shift; 31 if ((l_ByteValue > i_maxValue) || (l_ByteValue < i_minValue)) 32 { 33 logging::logMessage( 34 "Non valid Value encountered value[" + std::to_string(l_ByteValue) + 35 "] range [" + std::to_string(i_minValue) + ".." + 36 std::to_string(i_maxValue) + "] found "); 37 return false; 38 } 39 return l_isValid; 40 } 41 42 uint8_t DdimmVpdParser::getDdr5DensityPerDie(uint8_t i_ByteValue) 43 { 44 uint8_t l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED; 45 if (i_ByteValue < constants::VALUE_5) 46 { 47 l_densityPerDie = i_ByteValue * constants::VALUE_4; 48 } 49 else 50 { 51 switch (i_ByteValue) 52 { 53 case constants::VALUE_5: 54 l_densityPerDie = SDRAM_DENSITY_PER_DIE_24GB; 55 break; 56 57 case constants::VALUE_6: 58 l_densityPerDie = SDRAM_DENSITY_PER_DIE_32GB; 59 break; 60 61 case constants::VALUE_7: 62 l_densityPerDie = SDRAM_DENSITY_PER_DIE_48GB; 63 break; 64 65 case constants::VALUE_8: 66 l_densityPerDie = SDRAM_DENSITY_PER_DIE_64GB; 67 break; 68 69 default: 70 logging::logMessage( 71 "default value encountered for density per die"); 72 l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED; 73 break; 74 } 75 } 76 return l_densityPerDie; 77 } 78 79 uint8_t DdimmVpdParser::getDdr5DiePerPackage(uint8_t i_ByteValue) 80 { 81 uint8_t l_DiePerPackage = constants::VALUE_0; 82 if (i_ByteValue < constants::VALUE_2) 83 { 84 l_DiePerPackage = i_ByteValue + constants::VALUE_1; 85 } 86 else 87 { 88 l_DiePerPackage = 89 pow(constants::VALUE_2, (i_ByteValue - constants::VALUE_1)); 90 } 91 return l_DiePerPackage; 92 } 93 94 size_t DdimmVpdParser::getDdr5BasedDdimmSize( 95 types::BinaryVector::const_iterator i_iterator) 96 { 97 size_t l_dimmSize = 0; 98 99 do 100 { 101 if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] & 102 constants::MASK_BYTE_BITS_01, 103 constants::SHIFT_BITS_0, constants::VALUE_1, 104 constants::VALUE_3) || 105 !checkValidValue(i_iterator[constants::SPD_BYTE_235] & 106 constants::MASK_BYTE_BITS_345, 107 constants::SHIFT_BITS_3, constants::VALUE_1, 108 constants::VALUE_3)) 109 { 110 logging::logMessage( 111 "Capacity calculation failed for channels per DIMM. DDIMM Byte " 112 "235 value [" + 113 std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]"); 114 break; 115 } 116 uint8_t l_channelsPerPhy = 117 (((i_iterator[constants::SPD_BYTE_235] & 118 constants::MASK_BYTE_BITS_01) 119 ? constants::VALUE_1 120 : constants::VALUE_0) + 121 ((i_iterator[constants::SPD_BYTE_235] & 122 constants::MASK_BYTE_BITS_345) 123 ? constants::VALUE_1 124 : constants::VALUE_0)); 125 126 uint8_t l_channelsPerDdimm = 127 (((i_iterator[constants::SPD_BYTE_235] & 128 constants::MASK_BYTE_BIT_6) >> 129 constants::VALUE_6) + 130 ((i_iterator[constants::SPD_BYTE_235] & 131 constants::MASK_BYTE_BIT_7) >> 132 constants::VALUE_7)) * 133 l_channelsPerPhy; 134 135 if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] & 136 constants::MASK_BYTE_BITS_012, 137 constants::SHIFT_BITS_0, constants::VALUE_1, 138 constants::VALUE_3)) 139 { 140 logging::logMessage( 141 "Capacity calculation failed for bus width per channel. DDIMM " 142 "Byte 235 value [" + 143 std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]"); 144 break; 145 } 146 uint8_t l_busWidthPerChannel = 147 (i_iterator[constants::SPD_BYTE_235] & 148 constants::MASK_BYTE_BITS_012) 149 ? PRIMARY_BUS_WIDTH_32_BITS 150 : PRIMARY_BUS_WIDTH_UNUSED; 151 152 if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] & 153 constants::MASK_BYTE_BITS_567, 154 constants::SHIFT_BITS_5, constants::VALUE_0, 155 constants::VALUE_5)) 156 { 157 logging::logMessage( 158 "Capacity calculation failed for die per package. DDIMM Byte 4 " 159 "value [" + 160 std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]"); 161 break; 162 } 163 uint8_t l_diePerPackage = getDdr5DiePerPackage( 164 (i_iterator[constants::SPD_BYTE_4] & 165 constants::MASK_BYTE_BITS_567) >> 166 constants::VALUE_5); 167 168 if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] & 169 constants::MASK_BYTE_BITS_01234, 170 constants::SHIFT_BITS_0, constants::VALUE_1, 171 constants::VALUE_8)) 172 { 173 logging::logMessage( 174 "Capacity calculation failed for SDRAM Density per Die. DDIMM " 175 "Byte 4 value [" + 176 std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]"); 177 break; 178 } 179 uint8_t l_densityPerDie = getDdr5DensityPerDie( 180 i_iterator[constants::SPD_BYTE_4] & 181 constants::MASK_BYTE_BITS_01234); 182 183 uint8_t l_ranksPerChannel = 0; 184 185 if (((i_iterator[constants::SPD_BYTE_234] & 186 constants::MASK_BYTE_BIT_7) >> 187 constants::VALUE_7)) 188 { 189 l_ranksPerChannel = ((i_iterator[constants::SPD_BYTE_234] & 190 constants::MASK_BYTE_BITS_345) >> 191 constants::VALUE_3) + 192 constants::VALUE_1; 193 } 194 else if (((i_iterator[constants::SPD_BYTE_235] & 195 constants::MASK_BYTE_BIT_6) >> 196 constants::VALUE_6)) 197 { 198 l_ranksPerChannel = (i_iterator[constants::SPD_BYTE_234] & 199 constants::MASK_BYTE_BITS_012) + 200 constants::VALUE_1; 201 } 202 203 if (!checkValidValue(i_iterator[constants::SPD_BYTE_6] & 204 constants::MASK_BYTE_BITS_567, 205 constants::SHIFT_BITS_5, constants::VALUE_0, 206 constants::VALUE_3)) 207 { 208 logging::logMessage( 209 "Capacity calculation failed for dram width DDIMM Byte 6 value " 210 "[" + 211 std::to_string(i_iterator[constants::SPD_BYTE_6]) + "]"); 212 break; 213 } 214 uint8_t l_dramWidth = 215 constants::VALUE_4 * 216 (constants::VALUE_1 << ((i_iterator[constants::SPD_BYTE_6] & 217 constants::MASK_BYTE_BITS_567) >> 218 constants::VALUE_5)); 219 220 // DDIMM size is calculated in GB 221 l_dimmSize = (l_channelsPerDdimm * l_busWidthPerChannel * 222 l_diePerPackage * l_densityPerDie * l_ranksPerChannel) / 223 (8 * l_dramWidth); 224 225 } while (false); 226 227 return constants::CONVERT_GB_TO_KB * l_dimmSize; 228 } 229 230 size_t DdimmVpdParser::getDdr4BasedDdimmSize( 231 types::BinaryVector::const_iterator i_iterator) 232 { 233 size_t l_dimmSize = 0; 234 try 235 { 236 uint8_t l_tmpValue = 0; 237 238 // Calculate SDRAM capacity 239 l_tmpValue = i_iterator[constants::SPD_BYTE_4] & 240 constants::JEDEC_SDRAM_CAP_MASK; 241 242 /* Make sure the bits are not Reserved */ 243 if (l_tmpValue > constants::JEDEC_SDRAMCAP_RESERVED) 244 { 245 throw std::runtime_error( 246 "Bad data in VPD byte 4. Can't calculate SDRAM capacity and so " 247 "dimm size.\n "); 248 } 249 250 uint16_t l_sdramCapacity = 1; 251 l_sdramCapacity = (l_sdramCapacity << l_tmpValue) * 252 constants::JEDEC_SDRAMCAP_MULTIPLIER; 253 254 /* Calculate Primary bus width */ 255 l_tmpValue = i_iterator[constants::SPD_BYTE_13] & 256 constants::JEDEC_PRI_BUS_WIDTH_MASK; 257 258 if (l_tmpValue > constants::JEDEC_RESERVED_BITS) 259 { 260 throw std::runtime_error( 261 "Bad data in VPD byte 13. Can't calculate primary bus width " 262 "and so dimm size."); 263 } 264 265 uint8_t l_primaryBusWid = 1; 266 l_primaryBusWid = (l_primaryBusWid << l_tmpValue) * 267 constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER; 268 269 /* Calculate SDRAM width */ 270 l_tmpValue = i_iterator[constants::SPD_BYTE_12] & 271 constants::JEDEC_SDRAM_WIDTH_MASK; 272 273 if (l_tmpValue > constants::JEDEC_RESERVED_BITS) 274 { 275 throw std::runtime_error( 276 "Bad data in VPD byte 12. Can't calculate SDRAM width and so " 277 "dimm size."); 278 } 279 280 uint8_t l_sdramWidth = 1; 281 l_sdramWidth = (l_sdramWidth << l_tmpValue) * 282 constants::JEDEC_SDRAM_WIDTH_MULTIPLIER; 283 284 /* Calculate Number of ranks */ 285 l_tmpValue = i_iterator[constants::SPD_BYTE_12] & 286 constants::JEDEC_NUM_RANKS_MASK; 287 l_tmpValue >>= constants::JEDEC_RESERVED_BITS; 288 289 if (l_tmpValue > constants::JEDEC_RESERVED_BITS) 290 { 291 throw std::runtime_error( 292 "Bad data in VPD byte 12, can't calculate number of ranks. Invalid data found."); 293 } 294 295 uint8_t l_logicalRanksPerDimm = l_tmpValue + 1; 296 297 // Determine is single load stack (3DS) or not 298 l_tmpValue = i_iterator[constants::SPD_BYTE_6] & 299 constants::JEDEC_SIGNAL_LOADING_MASK; 300 301 if (l_tmpValue == constants::JEDEC_SINGLE_LOAD_STACK) 302 { 303 // Fetch die count 304 l_tmpValue = i_iterator[constants::SPD_BYTE_6] & 305 constants::JEDEC_DIE_COUNT_MASK; 306 l_tmpValue >>= constants::JEDEC_DIE_COUNT_RIGHT_SHIFT; 307 308 uint8_t l_dieCount = l_tmpValue + 1; 309 l_logicalRanksPerDimm *= l_dieCount; 310 } 311 312 l_dimmSize = 313 (l_sdramCapacity / constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER) * 314 (l_primaryBusWid / l_sdramWidth) * l_logicalRanksPerDimm; 315 316 // Converting dimm size from MB to KB 317 l_dimmSize *= constants::CONVERT_MB_TO_KB; 318 } 319 catch (const std::exception& l_ex) 320 { 321 // TODO:: Need an error log here 322 logging::logMessage("DDR4 DDIMM calculation is failed, reason: " + 323 std::string(l_ex.what())); 324 } 325 return l_dimmSize; 326 } 327 328 size_t DdimmVpdParser::getDdimmSize( 329 types::BinaryVector::const_iterator i_iterator) 330 { 331 size_t l_dimmSize = 0; 332 if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR5) 333 { 334 l_dimmSize = getDdr5BasedDdimmSize(i_iterator); 335 } 336 else if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR4) 337 { 338 l_dimmSize = getDdr4BasedDdimmSize(i_iterator); 339 } 340 else 341 { 342 logging::logMessage( 343 "Error: DDIMM is neither DDR4 nor DDR5. DDIMM Byte 2 value [" + 344 std::to_string(i_iterator[constants::SPD_BYTE_2]) + "]"); 345 } 346 return l_dimmSize; 347 } 348 349 void DdimmVpdParser::readKeywords( 350 types::BinaryVector::const_iterator i_iterator) 351 { 352 // collect DDIMM size value 353 auto l_dimmSize = getDdimmSize(i_iterator); 354 if (!l_dimmSize) 355 { 356 throw(DataException("Error: Calculated dimm size is 0.")); 357 } 358 359 m_parsedVpdMap.emplace("MemorySizeInKB", l_dimmSize); 360 // point the i_iterator to DIMM data and skip "11S" 361 advance(i_iterator, constants::DDIMM_11S_BARCODE_START + 362 constants::DDIMM_11S_FORMAT_LEN); 363 types::BinaryVector l_partNumber(i_iterator, 364 i_iterator + constants::PART_NUM_LEN); 365 366 advance(i_iterator, constants::PART_NUM_LEN); 367 types::BinaryVector l_serialNumber(i_iterator, 368 i_iterator + constants::SERIAL_NUM_LEN); 369 370 advance(i_iterator, constants::SERIAL_NUM_LEN); 371 types::BinaryVector l_ccin(i_iterator, i_iterator + constants::CCIN_LEN); 372 373 types::BinaryVector l_mfgId(DRAM_MANUFACTURER_ID_LENGTH); 374 std::copy_n((m_vpdVector.cbegin() + DRAM_MANUFACTURER_ID_OFFSET), 375 DRAM_MANUFACTURER_ID_LENGTH, l_mfgId.begin()); 376 377 m_parsedVpdMap.emplace("FN", l_partNumber); 378 m_parsedVpdMap.emplace("PN", move(l_partNumber)); 379 m_parsedVpdMap.emplace("SN", move(l_serialNumber)); 380 m_parsedVpdMap.emplace("CC", move(l_ccin)); 381 m_parsedVpdMap.emplace("DI", move(l_mfgId)); 382 } 383 384 types::VPDMapVariant DdimmVpdParser::parse() 385 { 386 try 387 { 388 // Read the data and return the map 389 auto l_iterator = m_vpdVector.cbegin(); 390 readKeywords(l_iterator); 391 return m_parsedVpdMap; 392 } 393 catch (const std::exception& exp) 394 { 395 logging::logMessage(exp.what()); 396 throw exp; 397 } 398 } 399 400 } // namespace vpd 401