1 #include "isdimm_vpd_parser.hpp" 2 3 #include <iostream> 4 #include <numeric> 5 #include <string> 6 7 namespace openpower 8 { 9 namespace vpd 10 { 11 namespace isdimm 12 { 13 namespace parser 14 { 15 static constexpr auto SPD_JEDEC_DDR4_SDRAM_CAP_MASK = 0x0F; 16 static constexpr auto SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MASK = 0x07; 17 static constexpr auto SPD_JEDEC_DDR4_SDRAM_WIDTH_MASK = 0x07; 18 static constexpr auto SPD_JEDEC_DDR4_NUM_RANKS_MASK = 0x38; 19 static constexpr auto SPD_JEDEC_DDR4_DIE_COUNT_MASK = 0x70; 20 static constexpr auto SPD_JEDEC_DDR4_SINGLE_LOAD_STACK = 0x02; 21 static constexpr auto SPD_JEDEC_DDR4_SIGNAL_LOADING_MASK = 0x03; 22 23 static constexpr auto SPD_JEDEC_DDR4_SDRAMCAP_MULTIPLIER = 256; 24 static constexpr auto SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER = 8; 25 static constexpr auto SPD_JEDEC_DDR4_SDRAM_WIDTH_MULTIPLIER = 4; 26 static constexpr auto SPD_JEDEC_DDR4_SDRAMCAP_RESERVED = 8; 27 static constexpr auto SPD_JEDEC_DDR4_4_RESERVED_BITS = 4; 28 static constexpr auto SPD_JEDEC_DDR4_3_RESERVED_BITS = 3; 29 static constexpr auto SPD_JEDEC_DDR4_DIE_COUNT_RIGHT_SHIFT = 4; 30 31 static constexpr auto SPD_JEDEC_DDR4_MFG_ID_MSB_OFFSET = 321; 32 static constexpr auto SPD_JEDEC_DDR4_MFG_ID_LSB_OFFSET = 320; 33 static constexpr auto SPD_JEDEC_DDR4_SN_BYTE0_OFFSET = 325; 34 static constexpr auto SPD_JEDEC_DDR4_SN_BYTE1_OFFSET = 326; 35 static constexpr auto SPD_JEDEC_DDR4_SN_BYTE2_OFFSET = 327; 36 static constexpr auto SPD_JEDEC_DDR4_SN_BYTE3_OFFSET = 328; 37 static constexpr auto SPD_JEDEC_DDR4_SDRAM_DENSITY_BANK_OFFSET = 4; 38 static constexpr auto SPD_JEDEC_DDR4_SDRAM_ADDR_OFFSET = 5; 39 static constexpr auto SPD_JEDEC_DDR4_DRAM_PRI_PACKAGE_OFFSET = 6; 40 static constexpr auto SPD_JEDEC_DDR4_DRAM_MODULE_ORG_OFFSET = 12; 41 42 // DDR5 JEDEC specification constants 43 static constexpr auto SPD_JEDEC_DDR5_SUB_CHANNELS_PER_DIMM = 235; 44 static constexpr auto SPD_JEDEC_DDR5_SUB_CHANNELS_PER_DIMM_MASK = 0x60; 45 static constexpr auto SPD_JEDEC_DDR5_PRI_BUS_WIDTH_PER_CHANNEL = 235; 46 static constexpr auto SPD_JEDEC_DDR5_PRI_BUS_WIDTH_PER_CHANNEL_MASK = 0x07; 47 static constexpr auto SPD_JEDEC_DDR5_SDRAM_IO_WIDTH_SYM_ALL = 6; 48 static constexpr auto SPD_JEDEC_DDR5_SDRAM_IO_WIDTH_ASYM_EVEN = 6; 49 static constexpr auto SPD_JEDEC_DDR5_SDRAM_IO_WIDTH_ASYM_ODD = 10; 50 static constexpr auto SPD_JEDEC_DDR5_SDRAM_IO_WIDTH_MASK = 0xE0; 51 static constexpr auto SPD_JEDEC_DDR5_DIE_PER_PKG_SYM_ALL = 4; 52 static constexpr auto SPD_JEDEC_DDR5_DIE_PER_PKG_ASYM_EVEN = 4; 53 static constexpr auto SPD_JEDEC_DDR5_DIE_PER_PKG_ASYM_ODD = 8; 54 static constexpr auto SPD_JEDEC_DDR5_DIE_PER_PKG_MASK = 0xE0; 55 static constexpr auto SPD_JEDEC_DDR5_SDRAM_DENSITY_PER_DIE_SYM_ALL = 4; 56 static constexpr auto SPD_JEDEC_DDR5_SDRAM_DENSITY_PER_DIE_ASYM_EVEN = 4; 57 static constexpr auto SPD_JEDEC_DDR5_SDRAM_DENSITY_PER_DIE_ASYM_ODD = 8; 58 static constexpr auto SPD_JEDEC_DDR5_SDRAM_DENSITY_PER_DIE_MASK = 0x1F; 59 static constexpr auto SPD_JEDEC_DDR5_RANK_MIX = 234; 60 static constexpr auto SPD_JEDEC_DDR5_RANK_MIX_SYMMETRICAL_MASK = 0x40; 61 62 auto isdimmVpdParser::getDDR4DimmCapacity(Binary::const_iterator& iterator) 63 { 64 size_t tmp = 0, dimmSize = 0; 65 66 size_t sdramCap = 1, priBusWid = 1, sdramWid = 1, logicalRanksPerDimm = 1; 67 Byte dieCount = 1; 68 69 // NOTE: This calculation is Only for DDR4 70 71 // Calculate SDRAM capacity 72 tmp = iterator[constants::SPD_BYTE_4] & SPD_JEDEC_DDR4_SDRAM_CAP_MASK; 73 /* Make sure the bits are not Reserved */ 74 if (tmp >= SPD_JEDEC_DDR4_SDRAMCAP_RESERVED) 75 { 76 std::cerr 77 << "Bad data in spd byte 4. Can't calculate SDRAM capacity and so " 78 "dimm size.\n "; 79 return dimmSize; 80 } 81 82 sdramCap = (sdramCap << tmp) * SPD_JEDEC_DDR4_SDRAMCAP_MULTIPLIER; 83 84 /* Calculate Primary bus width */ 85 tmp = iterator[constants::SPD_BYTE_13] & SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MASK; 86 if (tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS) 87 { 88 std::cerr 89 << "Bad data in spd byte 13. Can't calculate primary bus width " 90 "and so dimm size.\n "; 91 return dimmSize; 92 } 93 priBusWid = (priBusWid << tmp) * SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER; 94 95 /* Calculate SDRAM width */ 96 tmp = iterator[constants::SPD_BYTE_12] & SPD_JEDEC_DDR4_SDRAM_WIDTH_MASK; 97 if (tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS) 98 { 99 std::cerr 100 << "Bad data in vpd byte 12. Can't calculate SDRAM width and so " 101 "dimm size.\n "; 102 return dimmSize; 103 } 104 sdramWid = (sdramWid << tmp) * SPD_JEDEC_DDR4_SDRAM_WIDTH_MULTIPLIER; 105 106 tmp = iterator[constants::SPD_BYTE_6] & SPD_JEDEC_DDR4_SIGNAL_LOADING_MASK; 107 108 if (tmp == SPD_JEDEC_DDR4_SINGLE_LOAD_STACK) 109 { 110 // Fetch die count 111 tmp = iterator[constants::SPD_BYTE_6] & SPD_JEDEC_DDR4_DIE_COUNT_MASK; 112 tmp >>= SPD_JEDEC_DDR4_DIE_COUNT_RIGHT_SHIFT; 113 dieCount = tmp + 1; 114 } 115 116 /* Calculate Number of ranks */ 117 tmp = iterator[constants::SPD_BYTE_12] & SPD_JEDEC_DDR4_NUM_RANKS_MASK; 118 tmp >>= SPD_JEDEC_DDR4_3_RESERVED_BITS; 119 120 if (tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS) 121 { 122 std::cerr << "Can't calculate number of ranks. Invalid data found.\n "; 123 return dimmSize; 124 } 125 logicalRanksPerDimm = (tmp + 1) * dieCount; 126 127 dimmSize = (sdramCap / SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER) * 128 (priBusWid / sdramWid) * logicalRanksPerDimm; 129 130 return dimmSize; 131 } 132 133 auto isdimmVpdParser::getDDR4PartNumber(Binary::const_iterator& iterator) 134 { 135 char tmpPN[constants::PART_NUM_LEN + 1] = {'\0'}; 136 sprintf(tmpPN, "%02X%02X%02X%X", 137 iterator[SPD_JEDEC_DDR4_SDRAM_DENSITY_BANK_OFFSET], 138 iterator[SPD_JEDEC_DDR4_SDRAM_ADDR_OFFSET], 139 iterator[SPD_JEDEC_DDR4_DRAM_PRI_PACKAGE_OFFSET], 140 iterator[SPD_JEDEC_DDR4_DRAM_MODULE_ORG_OFFSET] & 0x0F); 141 std::string partNumber(tmpPN, sizeof(tmpPN) - 1); 142 return partNumber; 143 } 144 145 auto isdimmVpdParser::getDDR4SerialNumber(Binary::const_iterator& iterator) 146 { 147 char tmpSN[constants::SERIAL_NUM_LEN + 1] = {'\0'}; 148 sprintf(tmpSN, "%02X%02X%02X%02X%02X%02X", 149 iterator[SPD_JEDEC_DDR4_MFG_ID_MSB_OFFSET], 150 iterator[SPD_JEDEC_DDR4_MFG_ID_LSB_OFFSET], 151 iterator[SPD_JEDEC_DDR4_SN_BYTE0_OFFSET], 152 iterator[SPD_JEDEC_DDR4_SN_BYTE1_OFFSET], 153 iterator[SPD_JEDEC_DDR4_SN_BYTE2_OFFSET], 154 iterator[SPD_JEDEC_DDR4_SN_BYTE3_OFFSET]); 155 std::string serialNumber(tmpSN, sizeof(tmpSN) - 1); 156 return serialNumber; 157 } 158 159 auto isdimmVpdParser::getDDR4FruNumber(const std::string& partNumber, 160 Binary::const_iterator& iterator) 161 { 162 // check for 128GB ISRDIMM not implemented 163 //(128GB 2RX4(8GX72) IS RDIMM 36*(16GBIT, 2H),1.2V 288PIN,1.2" ROHS) - NA 164 165 // MTB Units is used in deciding the frequency of the DIMM 166 // This is applicable only for DDR4 specification 167 // 10 - DDR4-1600 168 // 9 - DDR4-1866 169 // 8 - DDR4-2133 170 // 7 - DDR4-2400 171 // 6 - DDR4-2666 172 // 5 - DDR4-3200 173 // pnFreqFnMap < tuple <partNumber, MTBUnits>, fruNumber> 174 static std::map<std::tuple<std::string, uint8_t>, std::string> pnFreqFnMap = 175 {{std::make_tuple("8421000", 6), "78P4191"}, 176 {std::make_tuple("8421008", 6), "78P4192"}, 177 {std::make_tuple("8529000", 6), "78P4197"}, 178 {std::make_tuple("8529008", 6), "78P4198"}, 179 {std::make_tuple("8529928", 6), "78P4199"}, 180 {std::make_tuple("8529B28", 6), "78P4200"}, 181 {std::make_tuple("8631928", 6), "78P6925"}, 182 {std::make_tuple("8529000", 5), "78P7317"}, 183 {std::make_tuple("8529008", 5), "78P7318"}, 184 {std::make_tuple("8631008", 5), "78P6815"}}; 185 186 std::string fruNumber; 187 uint8_t mtbUnits = iterator[openpower::vpd::constants::SPD_BYTE_18] & 188 openpower::vpd::constants::SPD_BYTE_MASK; 189 std::tuple<std::string, uint8_t> tup_key = {partNumber, mtbUnits}; 190 auto itr = pnFreqFnMap.find(tup_key); 191 if (itr != pnFreqFnMap.end()) 192 { 193 fruNumber = itr->second; 194 } 195 else 196 { 197 fruNumber = "FFFFFFF"; 198 } 199 return fruNumber; 200 } 201 202 auto isdimmVpdParser::getDDR4CCIN(const std::string& fruNumber) 203 { 204 static std::unordered_map<std::string, std::string> pnCCINMap = { 205 {"78P4191", "324D"}, {"78P4192", "324E"}, {"78P4197", "324E"}, 206 {"78P4198", "324F"}, {"78P4199", "325A"}, {"78P4200", "324C"}, 207 {"78P6925", "32BC"}, {"78P7317", "331A"}, {"78P7318", "331F"}, 208 {"78P6815", "32BB"}}; 209 210 std::string ccin; 211 auto itr = pnCCINMap.find(fruNumber); 212 if (itr != pnCCINMap.end()) 213 { 214 ccin = itr->second; 215 } 216 else 217 { 218 ccin = "XXXX"; 219 } 220 return ccin; 221 } 222 223 auto isdimmVpdParser::getDDR5DimmCapacity(Binary::const_iterator& iterator) 224 { 225 // dummy implementation to be updated when required 226 size_t dimmSize = 0; 227 (void)iterator; 228 return dimmSize; 229 } 230 231 auto isdimmVpdParser::getDDR5PartNumber(Binary::const_iterator& iterator) 232 { 233 // dummy implementation to be updated when required 234 std::string partNumber; 235 (void)iterator; 236 partNumber = "0123456"; 237 return partNumber; 238 } 239 240 auto isdimmVpdParser::getDDR5SerialNumber(Binary::const_iterator& iterator) 241 { 242 // dummy implementation to be updated when required 243 std::string serialNumber; 244 (void)iterator; 245 serialNumber = "444444444444"; 246 return serialNumber; 247 } 248 249 auto isdimmVpdParser::getDDR5FruNumber(const std::string& partNumber) 250 { 251 // dummy implementation to be updated when required 252 static std::unordered_map<std::string, std::string> pnFruMap = { 253 {"1234567", "XXXXXXX"}}; 254 255 std::string fruNumber; 256 auto itr = pnFruMap.find(partNumber); 257 if (itr != pnFruMap.end()) 258 { 259 fruNumber = itr->second; 260 } 261 else 262 { 263 fruNumber = "FFFFFFF"; 264 } 265 return fruNumber; 266 } 267 268 auto isdimmVpdParser::getDDR5CCIN(const std::string& partNumber) 269 { 270 // dummy implementation to be updated when required 271 static std::unordered_map<std::string, std::string> pnCCINMap = { 272 {"1234567", "XXXX"}}; 273 274 std::string ccin; 275 auto itr = pnCCINMap.find(partNumber); 276 if (itr != pnCCINMap.end()) 277 { 278 ccin = itr->second; 279 } 280 else 281 { 282 ccin = "XXXX"; 283 } 284 return ccin; 285 } 286 287 kwdVpdMap isdimmVpdParser::readKeywords(Binary::const_iterator& iterator) 288 { 289 inventory::KeywordVpdMap keywordValueMap{}; 290 if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) == 291 constants::SPD_DRAM_TYPE_DDR5) 292 { 293 size_t dimmSize = getDDR5DimmCapacity(iterator); 294 if (!dimmSize) 295 { 296 std::cerr << "Error: Calculated dimm size is 0."; 297 } 298 else 299 { 300 keywordValueMap.emplace("MemorySizeInKB", dimmSize); 301 } 302 auto partNumber = getDDR5PartNumber(iterator); 303 auto fruNumber = getDDR5FruNumber(partNumber); 304 keywordValueMap.emplace("FN", move(fruNumber)); 305 auto serialNumber = getDDR5SerialNumber(iterator); 306 keywordValueMap.emplace("SN", move(serialNumber)); 307 auto ccin = getDDR5CCIN(partNumber); 308 keywordValueMap.emplace("CC", move(ccin)); 309 keywordValueMap.emplace("PN", move(partNumber)); 310 } 311 else if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) == 312 constants::SPD_DRAM_TYPE_DDR4) 313 { 314 size_t dimmSize = getDDR4DimmCapacity(iterator); 315 if (!dimmSize) 316 { 317 std::cerr << "Error: Calculated dimm size is 0."; 318 } 319 else 320 { 321 keywordValueMap.emplace("MemorySizeInKB", 322 (dimmSize * constants::CONVERT_MB_TO_KB)); 323 } 324 325 auto partNumber = getDDR4PartNumber(iterator); 326 auto fruNumber = getDDR4FruNumber(partNumber, iterator); 327 auto serialNumber = getDDR4SerialNumber(iterator); 328 auto ccin = getDDR4CCIN(fruNumber); 329 // PN value is made same as FN value 330 auto displayPartNumber = fruNumber; 331 keywordValueMap.emplace("PN", move(displayPartNumber)); 332 keywordValueMap.emplace("FN", move(fruNumber)); 333 keywordValueMap.emplace("SN", move(serialNumber)); 334 keywordValueMap.emplace("CC", move(ccin)); 335 } 336 return keywordValueMap; 337 } 338 339 std::variant<kwdVpdMap, Store> isdimmVpdParser::parse() 340 { 341 // Read the data and return the map 342 auto iterator = memVpd.cbegin(); 343 auto vpdDataMap = readKeywords(iterator); 344 345 return vpdDataMap; 346 } 347 348 } // namespace parser 349 } // namespace isdimm 350 } // namespace vpd 351 } // namespace openpower 352