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