xref: /openbmc/openpower-vpd-parser/vpd-parser/memory_vpd_parser.cpp (revision abe481e189bf716ec0e1914159e8c25ac404d753)
1 #include "memory_vpd_parser.hpp"
2 
3 #include <cmath>
4 #include <cstdint>
5 #include <iostream>
6 #include <numeric>
7 #include <string>
8 
9 namespace openpower
10 {
11 namespace vpd
12 {
13 namespace memory
14 {
15 namespace parser
16 {
17 using namespace inventory;
18 using namespace constants;
19 using namespace std;
20 using namespace openpower::vpd::parser;
21 
22 static constexpr auto JEDEC_SDRAM_CAP_MASK = 0x0F;
23 static constexpr auto JEDEC_PRI_BUS_WIDTH_MASK = 0x07;
24 static constexpr auto JEDEC_SDRAM_WIDTH_MASK = 0x07;
25 static constexpr auto JEDEC_NUM_RANKS_MASK = 0x38;
26 static constexpr auto JEDEC_DIE_COUNT_MASK = 0x70;
27 static constexpr auto JEDEC_SINGLE_LOAD_STACK = 0x02;
28 static constexpr auto JEDEC_SIGNAL_LOADING_MASK = 0x03;
29 
30 static constexpr auto JEDEC_SDRAMCAP_MULTIPLIER = 256;
31 static constexpr auto JEDEC_PRI_BUS_WIDTH_MULTIPLIER = 8;
32 static constexpr auto JEDEC_SDRAM_WIDTH_MULTIPLIER = 4;
33 static constexpr auto JEDEC_SDRAMCAP_RESERVED = 6;
34 static constexpr auto JEDEC_RESERVED_BITS = 3;
35 static constexpr auto JEDEC_DIE_COUNT_RIGHT_SHIFT = 4;
36 
37 static constexpr auto SDRAM_DENSITY_PER_DIE_24GB = 24;
38 static constexpr auto SDRAM_DENSITY_PER_DIE_32GB = 32;
39 static constexpr auto SDRAM_DENSITY_PER_DIE_48GB = 48;
40 static constexpr auto SDRAM_DENSITY_PER_DIE_64GB = 64;
41 static constexpr auto SDRAM_DENSITY_PER_DIE_UNDEFINED = 0;
42 
43 static constexpr auto PRIMARY_BUS_WIDTH_32_BITS = 32;
44 static constexpr auto PRIMARY_BUS_WIDTH_UNUSED = 0;
45 
46 bool memoryVpdParser::checkValidValue(uint8_t l_ByteValue, uint8_t shift,
47                                       uint8_t minValue, uint8_t maxValue)
48 {
49     l_ByteValue = l_ByteValue >> shift;
50     if ((l_ByteValue > maxValue) || (l_ByteValue < minValue))
51     {
52         cout << "Non valid Value encountered value[" << l_ByteValue
53              << "] range [" << minValue << ".." << maxValue << "] found "
54              << " " << endl;
55         return false;
56     }
57     else
58     {
59         return true;
60     }
61 }
62 
63 uint8_t memoryVpdParser::getDDR5DensityPerDie(uint8_t l_ByteValue)
64 {
65     uint8_t l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED;
66     if (l_ByteValue < constants::VALUE_5)
67     {
68         l_densityPerDie = l_ByteValue * constants::VALUE_4;
69     }
70     else
71     {
72         switch (l_ByteValue)
73         {
74             case VALUE_5:
75                 l_densityPerDie = SDRAM_DENSITY_PER_DIE_24GB;
76                 break;
77 
78             case VALUE_6:
79                 l_densityPerDie = SDRAM_DENSITY_PER_DIE_32GB;
80                 break;
81 
82             case VALUE_7:
83                 l_densityPerDie = SDRAM_DENSITY_PER_DIE_48GB;
84                 break;
85 
86             case VALUE_8:
87                 l_densityPerDie = SDRAM_DENSITY_PER_DIE_64GB;
88                 break;
89 
90             default:
91                 cout << "default value encountered for density per die" << endl;
92                 l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED;
93                 break;
94         }
95     }
96     return l_densityPerDie;
97 }
98 
99 uint8_t memoryVpdParser::getDDR5DiePerPackage(uint8_t l_ByteValue)
100 {
101     uint8_t l_DiePerPackage = constants::VALUE_0;
102     if (l_ByteValue < constants::VALUE_2)
103     {
104         l_DiePerPackage = l_ByteValue + constants::VALUE_1;
105     }
106     else
107     {
108         l_DiePerPackage = pow(constants::VALUE_2,
109                               (l_ByteValue - constants::VALUE_1));
110     }
111     return l_DiePerPackage;
112 }
113 
114 auto memoryVpdParser::getDdr5BasedDDimmSize(Binary::const_iterator iterator)
115 {
116     size_t dimmSize = 0;
117 
118     do
119     {
120         if (!checkValidValue(iterator[constants::SPD_BYTE_235] &
121                                  constants::MASK_BYTE_BITS_01,
122                              constants::SHIFT_BITS_0, constants::VALUE_1,
123                              constants::VALUE_3) ||
124             !checkValidValue(iterator[constants::SPD_BYTE_235] &
125                                  constants::MASK_BYTE_BITS_345,
126                              constants::SHIFT_BITS_3, constants::VALUE_1,
127                              constants::VALUE_3))
128         {
129             std::cerr
130                 << "Capacity calculation failed for channels per DIMM. DDIMM Byte 235 value ["
131                 << iterator[constants::SPD_BYTE_235] << "]";
132             break;
133         }
134         uint8_t l_channelsPerDDimm =
135             (((iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BITS_01)
136                   ? constants::VALUE_1
137                   : constants::VALUE_0) +
138              ((iterator[constants::SPD_BYTE_235] &
139                constants::MASK_BYTE_BITS_345)
140                   ? constants::VALUE_1
141                   : constants::VALUE_0));
142 
143         if (!checkValidValue(iterator[constants::SPD_BYTE_235] &
144                                  constants::MASK_BYTE_BITS_012,
145                              constants::SHIFT_BITS_0, constants::VALUE_1,
146                              constants::VALUE_3))
147         {
148             std::cerr
149                 << "Capacity calculation failed for bus width per channel. DDIMM Byte 235 value ["
150                 << iterator[constants::SPD_BYTE_235] << "]";
151             break;
152         }
153         uint8_t l_busWidthPerChannel =
154             (iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BITS_012)
155                 ? PRIMARY_BUS_WIDTH_32_BITS
156                 : PRIMARY_BUS_WIDTH_UNUSED;
157 
158         if (!checkValidValue(iterator[constants::SPD_BYTE_4] &
159                                  constants::MASK_BYTE_BITS_567,
160                              constants::SHIFT_BITS_5, constants::VALUE_0,
161                              constants::VALUE_5))
162         {
163             std::cerr
164                 << "Capacity calculation failed for die per package. DDIMM Byte 4 value ["
165                 << iterator[constants::SPD_BYTE_4] << "]";
166             break;
167         }
168         uint8_t l_diePerPackage = getDDR5DiePerPackage(
169             (iterator[constants::SPD_BYTE_4] & constants::MASK_BYTE_BITS_567) >>
170             constants::VALUE_5);
171 
172         if (!checkValidValue(iterator[constants::SPD_BYTE_4] &
173                                  constants::MASK_BYTE_BITS_01234,
174                              constants::SHIFT_BITS_0, constants::VALUE_1,
175                              constants::VALUE_8))
176         {
177             std::cerr
178                 << "Capacity calculation failed for SDRAM Density per Die. DDIMM Byte 4 value ["
179                 << iterator[constants::SPD_BYTE_4] << "]";
180             break;
181         }
182         uint8_t l_densityPerDie = getDDR5DensityPerDie(
183             iterator[constants::SPD_BYTE_4] & constants::MASK_BYTE_BITS_01234);
184 
185         uint8_t l_ranksPerChannel = ((iterator[constants::SPD_BYTE_234] &
186                                       constants::MASK_BYTE_BITS_345) >>
187                                      constants::VALUE_3) +
188                                     (iterator[constants::SPD_BYTE_234] &
189                                      constants::MASK_BYTE_BITS_012) +
190                                     constants::VALUE_2;
191 
192         if (!checkValidValue(iterator[constants::SPD_BYTE_6] &
193                                  constants::MASK_BYTE_BITS_567,
194                              constants::SHIFT_BITS_5, constants::VALUE_0,
195                              constants::VALUE_3))
196         {
197             std::cout
198                 << "Capacity calculation failed for dram width DDIMM Byte 6 value ["
199                 << iterator[constants::SPD_BYTE_6] << "]";
200             break;
201         }
202         uint8_t l_dramWidth = VALUE_4 *
203                               (VALUE_1 << ((iterator[constants::SPD_BYTE_6] &
204                                             constants::MASK_BYTE_BITS_567) >>
205                                            constants::VALUE_5));
206 
207         dimmSize = (l_channelsPerDDimm * l_busWidthPerChannel *
208                     l_diePerPackage * l_densityPerDie * l_ranksPerChannel) /
209                    (8 * l_dramWidth);
210 
211     } while (1);
212 
213     return constants::CONVERT_MB_TO_KB * dimmSize;
214 }
215 
216 auto memoryVpdParser::getDdr4BasedDDimmSize(Binary::const_iterator iterator)
217 {
218     size_t tmp = 0, dimmSize = 0;
219 
220     size_t sdramCap = 1, priBusWid = 1, sdramWid = 1, logicalRanksPerDimm = 1;
221     Byte dieCount = 1;
222 
223     // NOTE: This calculation is Only for DDR4
224 
225     // Calculate SDRAM  capacity
226     tmp = iterator[SPD_BYTE_4] & JEDEC_SDRAM_CAP_MASK;
227     /* Make sure the bits are not Reserved */
228     if (tmp > JEDEC_SDRAMCAP_RESERVED)
229     {
230         cerr << "Bad data in vpd byte 4. Can't calculate SDRAM capacity and so "
231                 "dimm size.\n ";
232         return dimmSize;
233     }
234 
235     sdramCap = (sdramCap << tmp) * JEDEC_SDRAMCAP_MULTIPLIER;
236 
237     /* Calculate Primary bus width */
238     tmp = iterator[SPD_BYTE_13] & JEDEC_PRI_BUS_WIDTH_MASK;
239     if (tmp > JEDEC_RESERVED_BITS)
240     {
241         cerr << "Bad data in vpd byte 13. Can't calculate primary bus width "
242                 "and so dimm size.\n ";
243         return dimmSize;
244     }
245     priBusWid = (priBusWid << tmp) * JEDEC_PRI_BUS_WIDTH_MULTIPLIER;
246 
247     /* Calculate SDRAM width */
248     tmp = iterator[SPD_BYTE_12] & JEDEC_SDRAM_WIDTH_MASK;
249     if (tmp > JEDEC_RESERVED_BITS)
250     {
251         cerr << "Bad data in vpd byte 12. Can't calculate SDRAM width and so "
252                 "dimm size.\n ";
253         return dimmSize;
254     }
255     sdramWid = (sdramWid << tmp) * JEDEC_SDRAM_WIDTH_MULTIPLIER;
256 
257     tmp = iterator[SPD_BYTE_6] & JEDEC_SIGNAL_LOADING_MASK;
258 
259     if (tmp == JEDEC_SINGLE_LOAD_STACK)
260     {
261         // Fetch die count
262         tmp = iterator[SPD_BYTE_6] & JEDEC_DIE_COUNT_MASK;
263         tmp >>= JEDEC_DIE_COUNT_RIGHT_SHIFT;
264         dieCount = tmp + 1;
265     }
266 
267     /* Calculate Number of ranks */
268     tmp = iterator[SPD_BYTE_12] & JEDEC_NUM_RANKS_MASK;
269     tmp >>= JEDEC_RESERVED_BITS;
270 
271     if (tmp > JEDEC_RESERVED_BITS)
272     {
273         cerr << "Can't calculate number of ranks. Invalid data found.\n ";
274         return dimmSize;
275     }
276     logicalRanksPerDimm = (tmp + 1) * dieCount;
277 
278     dimmSize = (sdramCap / JEDEC_PRI_BUS_WIDTH_MULTIPLIER) *
279                (priBusWid / sdramWid) * logicalRanksPerDimm;
280 
281     return constants::CONVERT_MB_TO_KB * dimmSize;
282 }
283 
284 size_t memoryVpdParser::getDDimmSize(Binary::const_iterator iterator)
285 {
286     size_t dimmSize = 0;
287     if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
288         constants::SPD_DRAM_TYPE_DDR4)
289     {
290         dimmSize = getDdr4BasedDDimmSize(iterator);
291     }
292     else if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
293              constants::SPD_DRAM_TYPE_DDR5)
294     {
295         dimmSize = getDdr5BasedDDimmSize(iterator);
296     }
297     else
298     {
299         cerr << "Error: DDIMM is neither DDR4 nor DDR5. DDIMM Byte 2 value ["
300              << iterator[constants::SPD_BYTE_2] << "]";
301     }
302     return dimmSize;
303 }
304 
305 kwdVpdMap memoryVpdParser::readKeywords(Binary::const_iterator iterator)
306 {
307     KeywordVpdMap map{};
308 
309     // collect Dimm size value
310     auto dimmSize = getDDimmSize(iterator);
311     if (!dimmSize)
312     {
313         cerr << "Error: Calculated dimm size is 0.";
314     }
315 
316     map.emplace("MemorySizeInKB", dimmSize);
317     // point the iterator to DIMM data and skip "11S"
318     advance(iterator, MEMORY_VPD_DATA_START + 3);
319     Binary partNumber(iterator, iterator + PART_NUM_LEN);
320 
321     advance(iterator, PART_NUM_LEN);
322     Binary serialNumber(iterator, iterator + SERIAL_NUM_LEN);
323 
324     advance(iterator, SERIAL_NUM_LEN);
325     Binary ccin(iterator, iterator + CCIN_LEN);
326 
327     map.emplace("FN", partNumber);
328     map.emplace("PN", move(partNumber));
329     map.emplace("SN", move(serialNumber));
330     map.emplace("CC", move(ccin));
331 
332     return map;
333 }
334 
335 variant<kwdVpdMap, Store> memoryVpdParser::parse()
336 {
337     // Read the data and return the map
338     auto iterator = memVpd.cbegin();
339     auto vpdDataMap = readKeywords(iterator);
340 
341     return vpdDataMap;
342 }
343 
344 std::string memoryVpdParser::getInterfaceName() const
345 {
346     return memVpdInf;
347 }
348 
349 } // namespace parser
350 } // namespace memory
351 } // namespace vpd
352 } // namespace openpower
353