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     char tmpPN[constants::PART_NUM_LEN + 1] = {'\0'};
136     sprintf(tmpPN, "%02X%02X%02X%X",
137             iterator[SPD_JEDEC_DDR4_SDRAM_DENSITY_BANK_OFFSET],
138             iterator[SPD_JEDEC_DDR4_SDRAM_ADDR_OFFSET],
139             iterator[SPD_JEDEC_DDR4_DRAM_PRI_PACKAGE_OFFSET],
140             iterator[SPD_JEDEC_DDR4_DRAM_MODULE_ORG_OFFSET] & 0x0F);
141     std::string partNumber(tmpPN, sizeof(tmpPN) - 1);
142     return partNumber;
143 }
144 
145 auto isdimmVpdParser::getDDR4SerialNumber(Binary::const_iterator& iterator)
146 {
147     char tmpSN[constants::SERIAL_NUM_LEN + 1] = {'\0'};
148     sprintf(tmpSN, "%02X%02X%02X%02X%02X%02X",
149             iterator[SPD_JEDEC_DDR4_MFG_ID_MSB_OFFSET],
150             iterator[SPD_JEDEC_DDR4_MFG_ID_LSB_OFFSET],
151             iterator[SPD_JEDEC_DDR4_SN_BYTE0_OFFSET],
152             iterator[SPD_JEDEC_DDR4_SN_BYTE1_OFFSET],
153             iterator[SPD_JEDEC_DDR4_SN_BYTE2_OFFSET],
154             iterator[SPD_JEDEC_DDR4_SN_BYTE3_OFFSET]);
155     std::string serialNumber(tmpSN, sizeof(tmpSN) - 1);
156     return serialNumber;
157 }
158 
159 auto isdimmVpdParser::getDDR4FruNumber(const std::string& partNumber,
160                                        Binary::const_iterator& iterator)
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     // MTB Units is used in deciding the frequency of the DIMM
166     // This is applicable only for DDR4 specification
167     // 10 - DDR4-1600
168     // 9  - DDR4-1866
169     // 8  - DDR4-2133
170     // 7  - DDR4-2400
171     // 6  - DDR4-2666
172     // 5  - DDR4-3200
173     // pnFreqFnMap < tuple <partNumber, MTBUnits>, fruNumber>
174     static std::map<std::tuple<std::string, uint8_t>, std::string> pnFreqFnMap =
175         {{std::make_tuple("8421000", 6), "78P4191"},
176          {std::make_tuple("8421008", 6), "78P4192"},
177          {std::make_tuple("8529000", 6), "78P4197"},
178          {std::make_tuple("8529008", 6), "78P4198"},
179          {std::make_tuple("8529928", 6), "78P4199"},
180          {std::make_tuple("8529B28", 6), "78P4200"},
181          {std::make_tuple("8631928", 6), "78P6925"},
182          {std::make_tuple("8529000", 5), "78P7317"},
183          {std::make_tuple("8529008", 5), "78P7318"},
184          {std::make_tuple("8631008", 5), "78P6815"}};
185 
186     std::string fruNumber;
187     uint8_t mtbUnits = iterator[openpower::vpd::constants::SPD_BYTE_18] &
188                        openpower::vpd::constants::SPD_BYTE_MASK;
189     std::tuple<std::string, uint8_t> tup_key = {partNumber, mtbUnits};
190     auto itr = pnFreqFnMap.find(tup_key);
191     if (itr != pnFreqFnMap.end())
192     {
193         fruNumber = itr->second;
194     }
195     else
196     {
197         fruNumber = "FFFFFFF";
198     }
199     return fruNumber;
200 }
201 
202 auto isdimmVpdParser::getDDR4CCIN(const std::string& fruNumber)
203 {
204     static std::unordered_map<std::string, std::string> pnCCINMap = {
205         {"78P4191", "324D"}, {"78P4192", "324E"}, {"78P4197", "324E"},
206         {"78P4198", "324F"}, {"78P4199", "325A"}, {"78P4200", "324C"},
207         {"78P6925", "32BC"}, {"78P7317", "331A"}, {"78P7318", "331F"},
208         {"78P6815", "32BB"}};
209 
210     std::string ccin;
211     auto itr = pnCCINMap.find(fruNumber);
212     if (itr != pnCCINMap.end())
213     {
214         ccin = itr->second;
215     }
216     else
217     {
218         ccin = "XXXX";
219     }
220     return ccin;
221 }
222 
223 auto isdimmVpdParser::getDDR5DimmCapacity(Binary::const_iterator& iterator)
224 {
225     // dummy implementation to be updated when required
226     size_t dimmSize = 0;
227     (void)iterator;
228     return dimmSize;
229 }
230 
231 auto isdimmVpdParser::getDDR5PartNumber(Binary::const_iterator& iterator)
232 {
233     // dummy implementation to be updated when required
234     std::string partNumber;
235     (void)iterator;
236     partNumber = "0123456";
237     return partNumber;
238 }
239 
240 auto isdimmVpdParser::getDDR5SerialNumber(Binary::const_iterator& iterator)
241 {
242     // dummy implementation to be updated when required
243     std::string serialNumber;
244     (void)iterator;
245     serialNumber = "444444444444";
246     return serialNumber;
247 }
248 
249 auto isdimmVpdParser::getDDR5FruNumber(const std::string& partNumber)
250 {
251     // dummy implementation to be updated when required
252     static std::unordered_map<std::string, std::string> pnFruMap = {
253         {"1234567", "XXXXXXX"}};
254 
255     std::string fruNumber;
256     auto itr = pnFruMap.find(partNumber);
257     if (itr != pnFruMap.end())
258     {
259         fruNumber = itr->second;
260     }
261     else
262     {
263         fruNumber = "FFFFFFF";
264     }
265     return fruNumber;
266 }
267 
268 auto isdimmVpdParser::getDDR5CCIN(const std::string& partNumber)
269 {
270     // dummy implementation to be updated when required
271     static std::unordered_map<std::string, std::string> pnCCINMap = {
272         {"1234567", "XXXX"}};
273 
274     std::string ccin;
275     auto itr = pnCCINMap.find(partNumber);
276     if (itr != pnCCINMap.end())
277     {
278         ccin = itr->second;
279     }
280     else
281     {
282         ccin = "XXXX";
283     }
284     return ccin;
285 }
286 
287 kwdVpdMap isdimmVpdParser::readKeywords(Binary::const_iterator& iterator)
288 {
289     inventory::KeywordVpdMap keywordValueMap{};
290     if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
291         constants::SPD_DRAM_TYPE_DDR5)
292     {
293         size_t dimmSize = getDDR5DimmCapacity(iterator);
294         if (!dimmSize)
295         {
296             std::cerr << "Error: Calculated dimm size is 0.";
297         }
298         else
299         {
300             keywordValueMap.emplace("MemorySizeInKB", dimmSize);
301         }
302         auto partNumber = getDDR5PartNumber(iterator);
303         auto fruNumber = getDDR5FruNumber(partNumber);
304         keywordValueMap.emplace("FN", move(fruNumber));
305         auto serialNumber = getDDR5SerialNumber(iterator);
306         keywordValueMap.emplace("SN", move(serialNumber));
307         auto ccin = getDDR5CCIN(partNumber);
308         keywordValueMap.emplace("CC", move(ccin));
309         keywordValueMap.emplace("PN", move(partNumber));
310     }
311     else if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
312              constants::SPD_DRAM_TYPE_DDR4)
313     {
314         size_t dimmSize = getDDR4DimmCapacity(iterator);
315         if (!dimmSize)
316         {
317             std::cerr << "Error: Calculated dimm size is 0.";
318         }
319         else
320         {
321             keywordValueMap.emplace("MemorySizeInKB",
322                                     (dimmSize * constants::CONVERT_MB_TO_KB));
323         }
324 
325         auto partNumber = getDDR4PartNumber(iterator);
326         auto fruNumber = getDDR4FruNumber(partNumber, iterator);
327         auto serialNumber = getDDR4SerialNumber(iterator);
328         auto ccin = getDDR4CCIN(fruNumber);
329         // PN value is made same as FN value
330         auto displayPartNumber = fruNumber;
331         keywordValueMap.emplace("PN", move(displayPartNumber));
332         keywordValueMap.emplace("FN", move(fruNumber));
333         keywordValueMap.emplace("SN", move(serialNumber));
334         keywordValueMap.emplace("CC", move(ccin));
335     }
336     return keywordValueMap;
337 }
338 
339 std::variant<kwdVpdMap, Store> isdimmVpdParser::parse()
340 {
341     // Read the data and return the map
342     auto iterator = memVpd.cbegin();
343     auto vpdDataMap = readKeywords(iterator);
344 
345     return vpdDataMap;
346 }
347 
348 } // namespace parser
349 } // namespace isdimm
350 } // namespace vpd
351 } // namespace openpower
352