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