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 |