1 #include "memory_vpd_parser.hpp" 2 3 #include <cmath> 4 #include <cstdint> 5 #include <iostream> 6 #include <numeric> 7 #include <string> 8 9 namespace openpower 10 { 11 namespace vpd 12 { 13 namespace memory 14 { 15 namespace parser 16 { 17 using namespace inventory; 18 using namespace constants; 19 using namespace std; 20 using namespace openpower::vpd::parser; 21 22 static constexpr auto JEDEC_SDRAM_CAP_MASK = 0x0F; 23 static constexpr auto JEDEC_PRI_BUS_WIDTH_MASK = 0x07; 24 static constexpr auto JEDEC_SDRAM_WIDTH_MASK = 0x07; 25 static constexpr auto JEDEC_NUM_RANKS_MASK = 0x38; 26 static constexpr auto JEDEC_DIE_COUNT_MASK = 0x70; 27 static constexpr auto JEDEC_SINGLE_LOAD_STACK = 0x02; 28 static constexpr auto JEDEC_SIGNAL_LOADING_MASK = 0x03; 29 30 static constexpr auto JEDEC_SDRAMCAP_MULTIPLIER = 256; 31 static constexpr auto JEDEC_PRI_BUS_WIDTH_MULTIPLIER = 8; 32 static constexpr auto JEDEC_SDRAM_WIDTH_MULTIPLIER = 4; 33 static constexpr auto JEDEC_SDRAMCAP_RESERVED = 6; 34 static constexpr auto JEDEC_RESERVED_BITS = 3; 35 static constexpr auto JEDEC_DIE_COUNT_RIGHT_SHIFT = 4; 36 37 static constexpr auto SDRAM_DENSITY_PER_DIE_24GB = 24; 38 static constexpr auto SDRAM_DENSITY_PER_DIE_32GB = 32; 39 static constexpr auto SDRAM_DENSITY_PER_DIE_48GB = 48; 40 static constexpr auto SDRAM_DENSITY_PER_DIE_64GB = 64; 41 static constexpr auto SDRAM_DENSITY_PER_DIE_UNDEFINED = 0; 42 43 static constexpr auto PRIMARY_BUS_WIDTH_32_BITS = 32; 44 static constexpr auto PRIMARY_BUS_WIDTH_UNUSED = 0; 45 46 bool memoryVpdParser::checkValidValue(uint8_t l_ByteValue, uint8_t shift, 47 uint8_t minValue, uint8_t maxValue) 48 { 49 l_ByteValue = l_ByteValue >> shift; 50 if ((l_ByteValue > maxValue) || (l_ByteValue < minValue)) 51 { 52 cout << "Non valid Value encountered value[" << l_ByteValue 53 << "] range [" << minValue << ".." << maxValue << "] found " 54 << " " << endl; 55 return false; 56 } 57 else 58 { 59 return true; 60 } 61 } 62 63 uint8_t memoryVpdParser::getDDR5DensityPerDie(uint8_t l_ByteValue) 64 { 65 uint8_t l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED; 66 if (l_ByteValue < constants::VALUE_5) 67 { 68 l_densityPerDie = l_ByteValue * constants::VALUE_4; 69 } 70 else 71 { 72 switch (l_ByteValue) 73 { 74 case VALUE_5: 75 l_densityPerDie = SDRAM_DENSITY_PER_DIE_24GB; 76 break; 77 78 case VALUE_6: 79 l_densityPerDie = SDRAM_DENSITY_PER_DIE_32GB; 80 break; 81 82 case VALUE_7: 83 l_densityPerDie = SDRAM_DENSITY_PER_DIE_48GB; 84 break; 85 86 case VALUE_8: 87 l_densityPerDie = SDRAM_DENSITY_PER_DIE_64GB; 88 break; 89 90 default: 91 cout << "default value encountered for density per die" << endl; 92 l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED; 93 break; 94 } 95 } 96 return l_densityPerDie; 97 } 98 99 uint8_t memoryVpdParser::getDDR5DiePerPackage(uint8_t l_ByteValue) 100 { 101 uint8_t l_DiePerPackage = constants::VALUE_0; 102 if (l_ByteValue < constants::VALUE_2) 103 { 104 l_DiePerPackage = l_ByteValue + constants::VALUE_1; 105 } 106 else 107 { 108 l_DiePerPackage = pow(constants::VALUE_2, 109 (l_ByteValue - constants::VALUE_1)); 110 } 111 return l_DiePerPackage; 112 } 113 114 auto memoryVpdParser::getDdr5BasedDDimmSize(Binary::const_iterator iterator) 115 { 116 size_t dimmSize = 0; 117 118 do 119 { 120 if (!checkValidValue(iterator[constants::SPD_BYTE_235] & 121 constants::MASK_BYTE_BITS_01, 122 constants::SHIFT_BITS_0, constants::VALUE_1, 123 constants::VALUE_3) || 124 !checkValidValue(iterator[constants::SPD_BYTE_235] & 125 constants::MASK_BYTE_BITS_345, 126 constants::SHIFT_BITS_3, constants::VALUE_1, 127 constants::VALUE_3)) 128 { 129 std::cerr 130 << "Capacity calculation failed for channels per DIMM. DDIMM Byte 235 value [" 131 << iterator[constants::SPD_BYTE_235] << "]"; 132 break; 133 } 134 uint8_t l_channelsPerDDimm = 135 (((iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BITS_01) 136 ? constants::VALUE_1 137 : constants::VALUE_0) + 138 ((iterator[constants::SPD_BYTE_235] & 139 constants::MASK_BYTE_BITS_345) 140 ? constants::VALUE_1 141 : constants::VALUE_0)); 142 143 if (!checkValidValue(iterator[constants::SPD_BYTE_235] & 144 constants::MASK_BYTE_BITS_012, 145 constants::SHIFT_BITS_0, constants::VALUE_1, 146 constants::VALUE_3)) 147 { 148 std::cerr 149 << "Capacity calculation failed for bus width per channel. DDIMM Byte 235 value [" 150 << iterator[constants::SPD_BYTE_235] << "]"; 151 break; 152 } 153 uint8_t l_busWidthPerChannel = 154 (iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BITS_012) 155 ? PRIMARY_BUS_WIDTH_32_BITS 156 : PRIMARY_BUS_WIDTH_UNUSED; 157 158 if (!checkValidValue(iterator[constants::SPD_BYTE_4] & 159 constants::MASK_BYTE_BITS_567, 160 constants::SHIFT_BITS_5, constants::VALUE_0, 161 constants::VALUE_5)) 162 { 163 std::cerr 164 << "Capacity calculation failed for die per package. DDIMM Byte 4 value [" 165 << iterator[constants::SPD_BYTE_4] << "]"; 166 break; 167 } 168 uint8_t l_diePerPackage = getDDR5DiePerPackage( 169 (iterator[constants::SPD_BYTE_4] & constants::MASK_BYTE_BITS_567) >> 170 constants::VALUE_5); 171 172 if (!checkValidValue(iterator[constants::SPD_BYTE_4] & 173 constants::MASK_BYTE_BITS_01234, 174 constants::SHIFT_BITS_0, constants::VALUE_1, 175 constants::VALUE_8)) 176 { 177 std::cerr 178 << "Capacity calculation failed for SDRAM Density per Die. DDIMM Byte 4 value [" 179 << iterator[constants::SPD_BYTE_4] << "]"; 180 break; 181 } 182 uint8_t l_densityPerDie = getDDR5DensityPerDie( 183 iterator[constants::SPD_BYTE_4] & constants::MASK_BYTE_BITS_01234); 184 185 uint8_t l_ranksPerChannel = ((iterator[constants::SPD_BYTE_234] & 186 constants::MASK_BYTE_BITS_345) >> 187 constants::VALUE_3) + 188 (iterator[constants::SPD_BYTE_234] & 189 constants::MASK_BYTE_BITS_012) + 190 constants::VALUE_2; 191 192 if (!checkValidValue(iterator[constants::SPD_BYTE_6] & 193 constants::MASK_BYTE_BITS_567, 194 constants::SHIFT_BITS_5, constants::VALUE_0, 195 constants::VALUE_3)) 196 { 197 std::cout 198 << "Capacity calculation failed for dram width DDIMM Byte 6 value [" 199 << iterator[constants::SPD_BYTE_6] << "]"; 200 break; 201 } 202 uint8_t l_dramWidth = VALUE_4 * 203 (VALUE_1 << ((iterator[constants::SPD_BYTE_6] & 204 constants::MASK_BYTE_BITS_567) >> 205 constants::VALUE_5)); 206 207 dimmSize = (l_channelsPerDDimm * l_busWidthPerChannel * 208 l_diePerPackage * l_densityPerDie * l_ranksPerChannel) / 209 (8 * l_dramWidth); 210 211 } while (1); 212 213 return constants::CONVERT_MB_TO_KB * dimmSize; 214 } 215 216 auto memoryVpdParser::getDdr4BasedDDimmSize(Binary::const_iterator iterator) 217 { 218 size_t tmp = 0, dimmSize = 0; 219 220 size_t sdramCap = 1, priBusWid = 1, sdramWid = 1, logicalRanksPerDimm = 1; 221 Byte dieCount = 1; 222 223 // NOTE: This calculation is Only for DDR4 224 225 // Calculate SDRAM capacity 226 tmp = iterator[SPD_BYTE_4] & JEDEC_SDRAM_CAP_MASK; 227 /* Make sure the bits are not Reserved */ 228 if (tmp > JEDEC_SDRAMCAP_RESERVED) 229 { 230 cerr << "Bad data in vpd byte 4. Can't calculate SDRAM capacity and so " 231 "dimm size.\n "; 232 return dimmSize; 233 } 234 235 sdramCap = (sdramCap << tmp) * JEDEC_SDRAMCAP_MULTIPLIER; 236 237 /* Calculate Primary bus width */ 238 tmp = iterator[SPD_BYTE_13] & JEDEC_PRI_BUS_WIDTH_MASK; 239 if (tmp > JEDEC_RESERVED_BITS) 240 { 241 cerr << "Bad data in vpd byte 13. Can't calculate primary bus width " 242 "and so dimm size.\n "; 243 return dimmSize; 244 } 245 priBusWid = (priBusWid << tmp) * JEDEC_PRI_BUS_WIDTH_MULTIPLIER; 246 247 /* Calculate SDRAM width */ 248 tmp = iterator[SPD_BYTE_12] & JEDEC_SDRAM_WIDTH_MASK; 249 if (tmp > JEDEC_RESERVED_BITS) 250 { 251 cerr << "Bad data in vpd byte 12. Can't calculate SDRAM width and so " 252 "dimm size.\n "; 253 return dimmSize; 254 } 255 sdramWid = (sdramWid << tmp) * JEDEC_SDRAM_WIDTH_MULTIPLIER; 256 257 tmp = iterator[SPD_BYTE_6] & JEDEC_SIGNAL_LOADING_MASK; 258 259 if (tmp == JEDEC_SINGLE_LOAD_STACK) 260 { 261 // Fetch die count 262 tmp = iterator[SPD_BYTE_6] & JEDEC_DIE_COUNT_MASK; 263 tmp >>= JEDEC_DIE_COUNT_RIGHT_SHIFT; 264 dieCount = tmp + 1; 265 } 266 267 /* Calculate Number of ranks */ 268 tmp = iterator[SPD_BYTE_12] & JEDEC_NUM_RANKS_MASK; 269 tmp >>= JEDEC_RESERVED_BITS; 270 271 if (tmp > JEDEC_RESERVED_BITS) 272 { 273 cerr << "Can't calculate number of ranks. Invalid data found.\n "; 274 return dimmSize; 275 } 276 logicalRanksPerDimm = (tmp + 1) * dieCount; 277 278 dimmSize = (sdramCap / JEDEC_PRI_BUS_WIDTH_MULTIPLIER) * 279 (priBusWid / sdramWid) * logicalRanksPerDimm; 280 281 return constants::CONVERT_MB_TO_KB * dimmSize; 282 } 283 284 size_t memoryVpdParser::getDDimmSize(Binary::const_iterator iterator) 285 { 286 size_t dimmSize = 0; 287 if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) == 288 constants::SPD_DRAM_TYPE_DDR4) 289 { 290 dimmSize = getDdr4BasedDDimmSize(iterator); 291 } 292 else if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) == 293 constants::SPD_DRAM_TYPE_DDR5) 294 { 295 dimmSize = getDdr5BasedDDimmSize(iterator); 296 } 297 else 298 { 299 cerr << "Error: DDIMM is neither DDR4 nor DDR5. DDIMM Byte 2 value [" 300 << iterator[constants::SPD_BYTE_2] << "]"; 301 } 302 return dimmSize; 303 } 304 305 kwdVpdMap memoryVpdParser::readKeywords(Binary::const_iterator iterator) 306 { 307 KeywordVpdMap map{}; 308 309 // collect Dimm size value 310 auto dimmSize = getDDimmSize(iterator); 311 if (!dimmSize) 312 { 313 cerr << "Error: Calculated dimm size is 0."; 314 } 315 316 map.emplace("MemorySizeInKB", dimmSize); 317 // point the iterator to DIMM data and skip "11S" 318 advance(iterator, MEMORY_VPD_DATA_START + 3); 319 Binary partNumber(iterator, iterator + PART_NUM_LEN); 320 321 advance(iterator, PART_NUM_LEN); 322 Binary serialNumber(iterator, iterator + SERIAL_NUM_LEN); 323 324 advance(iterator, SERIAL_NUM_LEN); 325 Binary ccin(iterator, iterator + CCIN_LEN); 326 327 map.emplace("FN", partNumber); 328 map.emplace("PN", move(partNumber)); 329 map.emplace("SN", move(serialNumber)); 330 map.emplace("CC", move(ccin)); 331 332 return map; 333 } 334 335 variant<kwdVpdMap, Store> memoryVpdParser::parse() 336 { 337 // Read the data and return the map 338 auto iterator = memVpd.cbegin(); 339 auto vpdDataMap = readKeywords(iterator); 340 341 return vpdDataMap; 342 } 343 344 std::string memoryVpdParser::getInterfaceName() const 345 { 346 return memVpdInf; 347 } 348 349 } // namespace parser 350 } // namespace memory 351 } // namespace vpd 352 } // namespace openpower 353