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