| Sunny Srivastava | fa5e4d3 | 2023-03-12 11:59:49 -0500 | [diff] [blame] | 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; | 
| Priyanga Ramasamy | e606f06 | 2025-01-27 07:16:37 -0600 | [diff] [blame] | 43 | static constexpr auto SPD_JEDEC_DDR4_DRAM_MANUFACTURER_ID_OFFSET = 320; | 
|  | 44 | static constexpr auto SPD_JEDEC_DRAM_MANUFACTURER_ID_LENGTH = 2; | 
| Sunny Srivastava | fa5e4d3 | 2023-03-12 11:59:49 -0500 | [diff] [blame] | 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 |  | 
| Priyanga Ramasamy | e606f06 | 2025-01-27 07:16:37 -0600 | [diff] [blame] | 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 |  | 
| Sunny Srivastava | fa5e4d3 | 2023-03-12 11:59:49 -0500 | [diff] [blame] | 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; | 
| Priyanga Ramasamy | e606f06 | 2025-01-27 07:16:37 -0600 | [diff] [blame] | 313 | auto l_mfgId = getDDR4ManufacturerId(); | 
|  | 314 |  | 
| Sunny Srivastava | fa5e4d3 | 2023-03-12 11:59:49 -0500 | [diff] [blame] | 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()))); | 
| Priyanga Ramasamy | e606f06 | 2025-01-27 07:16:37 -0600 | [diff] [blame] | 323 | l_keywordValueMap.emplace("DI", move(l_mfgId)); | 
| Sunny Srivastava | fa5e4d3 | 2023-03-12 11:59:49 -0500 | [diff] [blame] | 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 |