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