xref: /openbmc/openpower-vpd-parser/vpd-manager/src/ddimm_parser.cpp (revision 43fedabc032ba7c209b58111b6c35c7d95a9f14e)
1 #include "ddimm_parser.hpp"
2 
3 #include "constants.hpp"
4 #include "exceptions.hpp"
5 
6 #include <cmath>
7 #include <cstdint>
8 #include <iostream>
9 #include <numeric>
10 #include <string>
11 
12 namespace vpd
13 {
14 
15 static constexpr auto SDRAM_DENSITY_PER_DIE_24GB = 24;
16 static constexpr auto SDRAM_DENSITY_PER_DIE_32GB = 32;
17 static constexpr auto SDRAM_DENSITY_PER_DIE_48GB = 48;
18 static constexpr auto SDRAM_DENSITY_PER_DIE_64GB = 64;
19 static constexpr auto SDRAM_DENSITY_PER_DIE_UNDEFINED = 0;
20 
21 static constexpr auto PRIMARY_BUS_WIDTH_32_BITS = 32;
22 static constexpr auto PRIMARY_BUS_WIDTH_UNUSED = 0;
23 static constexpr auto DRAM_MANUFACTURER_ID_OFFSET = 0x228;
24 static constexpr auto DRAM_MANUFACTURER_ID_LENGTH = 0x02;
25 
checkValidValue(uint8_t i_ByteValue,uint8_t i_shift,uint8_t i_minValue,uint8_t i_maxValue)26 bool DdimmVpdParser::checkValidValue(uint8_t i_ByteValue, uint8_t i_shift,
27                                      uint8_t i_minValue, uint8_t i_maxValue)
28 {
29     bool l_isValid = true;
30     uint8_t l_ByteValue = i_ByteValue >> i_shift;
31     if ((l_ByteValue > i_maxValue) || (l_ByteValue < i_minValue))
32     {
33         logging::logMessage(
34             "Non valid Value encountered value[" + std::to_string(l_ByteValue) +
35             "] range [" + std::to_string(i_minValue) + ".." +
36             std::to_string(i_maxValue) + "] found ");
37         return false;
38     }
39     return l_isValid;
40 }
41 
getDdr5DensityPerDie(uint8_t i_ByteValue)42 uint8_t DdimmVpdParser::getDdr5DensityPerDie(uint8_t i_ByteValue)
43 {
44     uint8_t l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED;
45     if (i_ByteValue < constants::VALUE_5)
46     {
47         l_densityPerDie = i_ByteValue * constants::VALUE_4;
48     }
49     else
50     {
51         switch (i_ByteValue)
52         {
53             case constants::VALUE_5:
54                 l_densityPerDie = SDRAM_DENSITY_PER_DIE_24GB;
55                 break;
56 
57             case constants::VALUE_6:
58                 l_densityPerDie = SDRAM_DENSITY_PER_DIE_32GB;
59                 break;
60 
61             case constants::VALUE_7:
62                 l_densityPerDie = SDRAM_DENSITY_PER_DIE_48GB;
63                 break;
64 
65             case constants::VALUE_8:
66                 l_densityPerDie = SDRAM_DENSITY_PER_DIE_64GB;
67                 break;
68 
69             default:
70                 logging::logMessage(
71                     "default value encountered for density per die");
72                 l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED;
73                 break;
74         }
75     }
76     return l_densityPerDie;
77 }
78 
getDdr5DiePerPackage(uint8_t i_ByteValue)79 uint8_t DdimmVpdParser::getDdr5DiePerPackage(uint8_t i_ByteValue)
80 {
81     uint8_t l_DiePerPackage = constants::VALUE_0;
82     if (i_ByteValue < constants::VALUE_2)
83     {
84         l_DiePerPackage = i_ByteValue + constants::VALUE_1;
85     }
86     else
87     {
88         l_DiePerPackage =
89             pow(constants::VALUE_2, (i_ByteValue - constants::VALUE_1));
90     }
91     return l_DiePerPackage;
92 }
93 
getDdr5BasedDdimmSize(types::BinaryVector::const_iterator i_iterator)94 size_t DdimmVpdParser::getDdr5BasedDdimmSize(
95     types::BinaryVector::const_iterator i_iterator)
96 {
97     size_t l_dimmSize = 0;
98 
99     do
100     {
101         if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] &
102                                  constants::MASK_BYTE_BITS_01,
103                              constants::SHIFT_BITS_0, constants::VALUE_1,
104                              constants::VALUE_3) ||
105             !checkValidValue(i_iterator[constants::SPD_BYTE_235] &
106                                  constants::MASK_BYTE_BITS_345,
107                              constants::SHIFT_BITS_3, constants::VALUE_1,
108                              constants::VALUE_3))
109         {
110             logging::logMessage(
111                 "Capacity calculation failed for channels per DIMM. DDIMM Byte "
112                 "235 value [" +
113                 std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]");
114             break;
115         }
116         uint8_t l_channelsPerPhy =
117             (((i_iterator[constants::SPD_BYTE_235] &
118                constants::MASK_BYTE_BITS_01)
119                   ? constants::VALUE_1
120                   : constants::VALUE_0) +
121              ((i_iterator[constants::SPD_BYTE_235] &
122                constants::MASK_BYTE_BITS_345)
123                   ? constants::VALUE_1
124                   : constants::VALUE_0));
125 
126         uint8_t l_channelsPerDdimm =
127             (((i_iterator[constants::SPD_BYTE_235] &
128                constants::MASK_BYTE_BIT_6) >>
129               constants::VALUE_6) +
130              ((i_iterator[constants::SPD_BYTE_235] &
131                constants::MASK_BYTE_BIT_7) >>
132               constants::VALUE_7)) *
133             l_channelsPerPhy;
134 
135         if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] &
136                                  constants::MASK_BYTE_BITS_012,
137                              constants::SHIFT_BITS_0, constants::VALUE_1,
138                              constants::VALUE_3))
139         {
140             logging::logMessage(
141                 "Capacity calculation failed for bus width per channel. DDIMM "
142                 "Byte 235 value [" +
143                 std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]");
144             break;
145         }
146         uint8_t l_busWidthPerChannel =
147             (i_iterator[constants::SPD_BYTE_235] &
148              constants::MASK_BYTE_BITS_012)
149                 ? PRIMARY_BUS_WIDTH_32_BITS
150                 : PRIMARY_BUS_WIDTH_UNUSED;
151 
152         if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] &
153                                  constants::MASK_BYTE_BITS_567,
154                              constants::SHIFT_BITS_5, constants::VALUE_0,
155                              constants::VALUE_5))
156         {
157             logging::logMessage(
158                 "Capacity calculation failed for die per package. DDIMM Byte 4 "
159                 "value [" +
160                 std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]");
161             break;
162         }
163         uint8_t l_diePerPackage = getDdr5DiePerPackage(
164             (i_iterator[constants::SPD_BYTE_4] &
165              constants::MASK_BYTE_BITS_567) >>
166             constants::VALUE_5);
167 
168         if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] &
169                                  constants::MASK_BYTE_BITS_01234,
170                              constants::SHIFT_BITS_0, constants::VALUE_1,
171                              constants::VALUE_8))
172         {
173             logging::logMessage(
174                 "Capacity calculation failed for SDRAM Density per Die. DDIMM "
175                 "Byte 4 value [" +
176                 std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]");
177             break;
178         }
179         uint8_t l_densityPerDie = getDdr5DensityPerDie(
180             i_iterator[constants::SPD_BYTE_4] &
181             constants::MASK_BYTE_BITS_01234);
182 
183         uint8_t l_ranksPerChannel = 0;
184 
185         if (((i_iterator[constants::SPD_BYTE_234] &
186               constants::MASK_BYTE_BIT_7) >>
187              constants::VALUE_7))
188         {
189             l_ranksPerChannel = ((i_iterator[constants::SPD_BYTE_234] &
190                                   constants::MASK_BYTE_BITS_345) >>
191                                  constants::VALUE_3) +
192                                 constants::VALUE_1;
193         }
194         else if (((i_iterator[constants::SPD_BYTE_235] &
195                    constants::MASK_BYTE_BIT_6) >>
196                   constants::VALUE_6))
197         {
198             l_ranksPerChannel = (i_iterator[constants::SPD_BYTE_234] &
199                                  constants::MASK_BYTE_BITS_012) +
200                                 constants::VALUE_1;
201         }
202 
203         if (!checkValidValue(i_iterator[constants::SPD_BYTE_6] &
204                                  constants::MASK_BYTE_BITS_567,
205                              constants::SHIFT_BITS_5, constants::VALUE_0,
206                              constants::VALUE_3))
207         {
208             logging::logMessage(
209                 "Capacity calculation failed for dram width DDIMM Byte 6 value "
210                 "[" +
211                 std::to_string(i_iterator[constants::SPD_BYTE_6]) + "]");
212             break;
213         }
214         uint8_t l_dramWidth =
215             constants::VALUE_4 *
216             (constants::VALUE_1 << ((i_iterator[constants::SPD_BYTE_6] &
217                                      constants::MASK_BYTE_BITS_567) >>
218                                     constants::VALUE_5));
219 
220         // DDIMM size is calculated in GB
221         l_dimmSize = (l_channelsPerDdimm * l_busWidthPerChannel *
222                       l_diePerPackage * l_densityPerDie * l_ranksPerChannel) /
223                      (8 * l_dramWidth);
224 
225     } while (false);
226 
227     return constants::CONVERT_GB_TO_KB * l_dimmSize;
228 }
229 
getDdr4BasedDdimmSize(types::BinaryVector::const_iterator i_iterator)230 size_t DdimmVpdParser::getDdr4BasedDdimmSize(
231     types::BinaryVector::const_iterator i_iterator)
232 {
233     size_t l_dimmSize = 0;
234     try
235     {
236         uint8_t l_tmpValue = 0;
237 
238         // Calculate SDRAM capacity
239         l_tmpValue = i_iterator[constants::SPD_BYTE_4] &
240                      constants::JEDEC_SDRAM_CAP_MASK;
241 
242         /* Make sure the bits are not Reserved */
243         if (l_tmpValue > constants::JEDEC_SDRAMCAP_RESERVED)
244         {
245             throw std::runtime_error(
246                 "Bad data in VPD byte 4. Can't calculate SDRAM capacity and so "
247                 "dimm size.\n ");
248         }
249 
250         uint16_t l_sdramCapacity = 1;
251         l_sdramCapacity = (l_sdramCapacity << l_tmpValue) *
252                           constants::JEDEC_SDRAMCAP_MULTIPLIER;
253 
254         /* Calculate Primary bus width */
255         l_tmpValue = i_iterator[constants::SPD_BYTE_13] &
256                      constants::JEDEC_PRI_BUS_WIDTH_MASK;
257 
258         if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
259         {
260             throw std::runtime_error(
261                 "Bad data in VPD byte 13. Can't calculate primary bus width "
262                 "and so dimm size.");
263         }
264 
265         uint8_t l_primaryBusWid = 1;
266         l_primaryBusWid = (l_primaryBusWid << l_tmpValue) *
267                           constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER;
268 
269         /* Calculate SDRAM width */
270         l_tmpValue = i_iterator[constants::SPD_BYTE_12] &
271                      constants::JEDEC_SDRAM_WIDTH_MASK;
272 
273         if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
274         {
275             throw std::runtime_error(
276                 "Bad data in VPD byte 12. Can't calculate SDRAM width and so "
277                 "dimm size.");
278         }
279 
280         uint8_t l_sdramWidth = 1;
281         l_sdramWidth = (l_sdramWidth << l_tmpValue) *
282                        constants::JEDEC_SDRAM_WIDTH_MULTIPLIER;
283 
284         /* Calculate Number of ranks */
285         l_tmpValue = i_iterator[constants::SPD_BYTE_12] &
286                      constants::JEDEC_NUM_RANKS_MASK;
287         l_tmpValue >>= constants::JEDEC_RESERVED_BITS;
288 
289         if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
290         {
291             throw std::runtime_error(
292                 "Bad data in VPD byte 12, can't calculate number of ranks. Invalid data found.");
293         }
294 
295         uint8_t l_logicalRanksPerDimm = l_tmpValue + 1;
296 
297         // Determine is single load stack (3DS) or not
298         l_tmpValue = i_iterator[constants::SPD_BYTE_6] &
299                      constants::JEDEC_SIGNAL_LOADING_MASK;
300 
301         if (l_tmpValue == constants::JEDEC_SINGLE_LOAD_STACK)
302         {
303             // Fetch die count
304             l_tmpValue = i_iterator[constants::SPD_BYTE_6] &
305                          constants::JEDEC_DIE_COUNT_MASK;
306             l_tmpValue >>= constants::JEDEC_DIE_COUNT_RIGHT_SHIFT;
307 
308             uint8_t l_dieCount = l_tmpValue + 1;
309             l_logicalRanksPerDimm *= l_dieCount;
310         }
311 
312         l_dimmSize =
313             (l_sdramCapacity / constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER) *
314             (l_primaryBusWid / l_sdramWidth) * l_logicalRanksPerDimm;
315 
316         // Converting dimm size from MB to KB
317         l_dimmSize *= constants::CONVERT_MB_TO_KB;
318     }
319     catch (const std::exception& l_ex)
320     {
321         // TODO:: Need an error log here
322         logging::logMessage("DDR4 DDIMM calculation is failed, reason: " +
323                             std::string(l_ex.what()));
324     }
325     return l_dimmSize;
326 }
327 
getDdimmSize(types::BinaryVector::const_iterator i_iterator)328 size_t DdimmVpdParser::getDdimmSize(
329     types::BinaryVector::const_iterator i_iterator)
330 {
331     size_t l_dimmSize = 0;
332     if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR5)
333     {
334         l_dimmSize = getDdr5BasedDdimmSize(i_iterator);
335     }
336     else if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR4)
337     {
338         l_dimmSize = getDdr4BasedDdimmSize(i_iterator);
339     }
340     else
341     {
342         logging::logMessage(
343             "Error: DDIMM is neither DDR4 nor DDR5. DDIMM Byte 2 value [" +
344             std::to_string(i_iterator[constants::SPD_BYTE_2]) + "]");
345     }
346     return l_dimmSize;
347 }
348 
readKeywords(types::BinaryVector::const_iterator i_iterator)349 void DdimmVpdParser::readKeywords(
350     types::BinaryVector::const_iterator i_iterator)
351 {
352     // collect DDIMM size value
353     auto l_dimmSize = getDdimmSize(i_iterator);
354     if (!l_dimmSize)
355     {
356         throw(DataException("Error: Calculated dimm size is 0."));
357     }
358 
359     m_parsedVpdMap.emplace("MemorySizeInKB", l_dimmSize);
360     // point the i_iterator to DIMM data and skip "11S"
361     advance(i_iterator, constants::DDIMM_11S_BARCODE_START +
362                             constants::DDIMM_11S_FORMAT_LEN);
363     types::BinaryVector l_partNumber(i_iterator,
364                                      i_iterator + constants::PART_NUM_LEN);
365 
366     advance(i_iterator, constants::PART_NUM_LEN);
367     types::BinaryVector l_serialNumber(i_iterator,
368                                        i_iterator + constants::SERIAL_NUM_LEN);
369 
370     advance(i_iterator, constants::SERIAL_NUM_LEN);
371     types::BinaryVector l_ccin(i_iterator, i_iterator + constants::CCIN_LEN);
372 
373     types::BinaryVector l_mfgId(DRAM_MANUFACTURER_ID_LENGTH);
374     std::copy_n((m_vpdVector.cbegin() + DRAM_MANUFACTURER_ID_OFFSET),
375                 DRAM_MANUFACTURER_ID_LENGTH, l_mfgId.begin());
376 
377     m_parsedVpdMap.emplace("FN", l_partNumber);
378     m_parsedVpdMap.emplace("PN", move(l_partNumber));
379     m_parsedVpdMap.emplace("SN", move(l_serialNumber));
380     m_parsedVpdMap.emplace("CC", move(l_ccin));
381     m_parsedVpdMap.emplace("DI", move(l_mfgId));
382 }
383 
parse()384 types::VPDMapVariant DdimmVpdParser::parse()
385 {
386     try
387     {
388         // Read the data and return the map
389         auto l_iterator = m_vpdVector.cbegin();
390         readKeywords(l_iterator);
391         return m_parsedVpdMap;
392     }
393     catch (const std::exception& exp)
394     {
395         logging::logMessage(exp.what());
396         throw exp;
397     }
398 }
399 
400 } // namespace vpd
401