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