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