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