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