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 = pow(constants::VALUE_2,
111 (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 = (((iterator[constants::SPD_BYTE_235] &
147 constants::MASK_BYTE_BITS_6) >>
148 constants::VALUE_6) +
149 ((iterator[constants::SPD_BYTE_235] &
150 constants::MASK_BYTE_BITS_7) >>
151 constants::VALUE_7)) *
152 l_channelsPerPhy;
153
154 if (!checkValidValue(iterator[constants::SPD_BYTE_235] &
155 constants::MASK_BYTE_BITS_012,
156 constants::SHIFT_BITS_0, constants::VALUE_1,
157 constants::VALUE_3))
158 {
159 std::cerr
160 << "Capacity calculation failed for bus width per channel. "
161 "DDIMM Byte 235 value ["
162 << iterator[constants::SPD_BYTE_235] << "]";
163 break;
164 }
165 uint8_t l_busWidthPerChannel =
166 (iterator[constants::SPD_BYTE_235] & constants::MASK_BYTE_BITS_012)
167 ? PRIMARY_BUS_WIDTH_32_BITS
168 : PRIMARY_BUS_WIDTH_UNUSED;
169
170 if (!checkValidValue(iterator[constants::SPD_BYTE_4] &
171 constants::MASK_BYTE_BITS_567,
172 constants::SHIFT_BITS_5, constants::VALUE_0,
173 constants::VALUE_5))
174 {
175 std::cerr
176 << "Capacity calculation failed for die per package. DDIMM "
177 "Byte 4 value ["
178 << iterator[constants::SPD_BYTE_4] << "]";
179 break;
180 }
181 uint8_t l_diePerPackage = getDDR5DiePerPackage(
182 (iterator[constants::SPD_BYTE_4] & constants::MASK_BYTE_BITS_567) >>
183 constants::VALUE_5);
184
185 if (!checkValidValue(iterator[constants::SPD_BYTE_4] &
186 constants::MASK_BYTE_BITS_01234,
187 constants::SHIFT_BITS_0, constants::VALUE_1,
188 constants::VALUE_8))
189 {
190 std::cerr
191 << "Capacity calculation failed for SDRAM Density per Die. "
192 "DDIMM Byte 4 value ["
193 << iterator[constants::SPD_BYTE_4] << "]";
194 break;
195 }
196 uint8_t l_densityPerDie = getDDR5DensityPerDie(
197 iterator[constants::SPD_BYTE_4] & constants::MASK_BYTE_BITS_01234);
198
199 uint8_t l_ranksPerChannel = 0;
200 if (((iterator[constants::SPD_BYTE_235] &
201 constants::MASK_BYTE_BITS_7) >>
202 constants::VALUE_7))
203 {
204 l_ranksPerChannel = ((iterator[constants::SPD_BYTE_234] &
205 constants::MASK_BYTE_BITS_345) >>
206 constants::VALUE_3) +
207 constants::VALUE_1;
208 }
209 else if (((iterator[constants::SPD_BYTE_235] &
210 constants::MASK_BYTE_BITS_6) >>
211 constants::VALUE_6))
212 {
213 l_ranksPerChannel = (iterator[constants::SPD_BYTE_234] &
214 constants::MASK_BYTE_BITS_012) +
215 constants::VALUE_1;
216 }
217 #if 0
218 // Old Style capacity calculation kept for reference
219 // will be removed later
220 uint8_t l_ranksPerChannel =
221 (((iterator[constants::SPD_BYTE_234] &
222 constants::MASK_BYTE_BITS_345) >>
223 constants::VALUE_3) *
224 ((iterator[constants::SPD_BYTE_235] &
225 constants::MASK_BYTE_BITS_7) >>
226 constants::VALUE_7)) +
227 ((iterator[constants::SPD_BYTE_234] &
228 constants::MASK_BYTE_BITS_012) +
229 constants::VALUE_2 * ((iterator[constants::SPD_BYTE_235] &
230 constants::MASK_BYTE_BITS_6) >>
231 constants::VALUE_6));
232 #endif
233
234 if (!checkValidValue(iterator[constants::SPD_BYTE_6] &
235 constants::MASK_BYTE_BITS_567,
236 constants::SHIFT_BITS_5, constants::VALUE_0,
237 constants::VALUE_3))
238 {
239 std::cout
240 << "Capacity calculation failed for dram width DDIMM Byte 6 value ["
241 << iterator[constants::SPD_BYTE_6] << "]";
242 break;
243 }
244 uint8_t l_dramWidth = VALUE_4 *
245 (VALUE_1 << ((iterator[constants::SPD_BYTE_6] &
246 constants::MASK_BYTE_BITS_567) >>
247 constants::VALUE_5));
248
249 dimmSize = (l_channelsPerDdimm * l_busWidthPerChannel *
250 l_diePerPackage * l_densityPerDie * l_ranksPerChannel) /
251 (8 * l_dramWidth);
252
253 } while (false);
254
255 return constants::CONVERT_GB_TO_KB * dimmSize;
256 }
257
getDdr4BasedDDimmSize(Binary::const_iterator iterator)258 auto memoryVpdParser::getDdr4BasedDDimmSize(Binary::const_iterator iterator)
259 {
260 size_t tmp = 0, dimmSize = 0;
261
262 size_t sdramCap = 1, priBusWid = 1, sdramWid = 1, logicalRanksPerDimm = 1;
263 Byte dieCount = 1;
264
265 // NOTE: This calculation is Only for DDR4
266
267 // Calculate SDRAM capacity
268 tmp = iterator[SPD_BYTE_4] & JEDEC_SDRAM_CAP_MASK;
269 /* Make sure the bits are not Reserved */
270 if (tmp > JEDEC_SDRAMCAP_RESERVED)
271 {
272 cerr << "Bad data in vpd byte 4. Can't calculate SDRAM capacity and so "
273 "dimm size.\n ";
274 return dimmSize;
275 }
276
277 sdramCap = (sdramCap << tmp) * JEDEC_SDRAMCAP_MULTIPLIER;
278
279 /* Calculate Primary bus width */
280 tmp = iterator[SPD_BYTE_13] & JEDEC_PRI_BUS_WIDTH_MASK;
281 if (tmp > JEDEC_RESERVED_BITS)
282 {
283 cerr << "Bad data in vpd byte 13. Can't calculate primary bus width "
284 "and so dimm size.\n ";
285 return dimmSize;
286 }
287 priBusWid = (priBusWid << tmp) * JEDEC_PRI_BUS_WIDTH_MULTIPLIER;
288
289 /* Calculate SDRAM width */
290 tmp = iterator[SPD_BYTE_12] & JEDEC_SDRAM_WIDTH_MASK;
291 if (tmp > JEDEC_RESERVED_BITS)
292 {
293 cerr << "Bad data in vpd byte 12. Can't calculate SDRAM width and so "
294 "dimm size.\n ";
295 return dimmSize;
296 }
297 sdramWid = (sdramWid << tmp) * JEDEC_SDRAM_WIDTH_MULTIPLIER;
298
299 tmp = iterator[SPD_BYTE_6] & JEDEC_SIGNAL_LOADING_MASK;
300
301 if (tmp == JEDEC_SINGLE_LOAD_STACK)
302 {
303 // Fetch die count
304 tmp = iterator[SPD_BYTE_6] & JEDEC_DIE_COUNT_MASK;
305 tmp >>= JEDEC_DIE_COUNT_RIGHT_SHIFT;
306 dieCount = tmp + 1;
307 }
308
309 /* Calculate Number of ranks */
310 tmp = iterator[SPD_BYTE_12] & JEDEC_NUM_RANKS_MASK;
311 tmp >>= JEDEC_RESERVED_BITS;
312
313 if (tmp > JEDEC_RESERVED_BITS)
314 {
315 cerr << "Can't calculate number of ranks. Invalid data found.\n ";
316 return dimmSize;
317 }
318 logicalRanksPerDimm = (tmp + 1) * dieCount;
319
320 dimmSize = (sdramCap / JEDEC_PRI_BUS_WIDTH_MULTIPLIER) *
321 (priBusWid / sdramWid) * logicalRanksPerDimm;
322
323 return constants::CONVERT_MB_TO_KB * dimmSize;
324 }
325
getDDimmSize(Binary::const_iterator iterator)326 size_t memoryVpdParser::getDDimmSize(Binary::const_iterator iterator)
327 {
328 size_t dimmSize = 0;
329 if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
330 constants::SPD_DRAM_TYPE_DDR4)
331 {
332 dimmSize = getDdr4BasedDDimmSize(iterator);
333 }
334 else if ((iterator[constants::SPD_BYTE_2] & constants::SPD_BYTE_MASK) ==
335 constants::SPD_DRAM_TYPE_DDR5)
336 {
337 dimmSize = getDdr5BasedDDimmSize(iterator);
338 }
339 else
340 {
341 cerr << "Error: DDIMM is neither DDR4 nor DDR5. DDIMM Byte 2 value ["
342 << iterator[constants::SPD_BYTE_2] << "]";
343 }
344 return dimmSize;
345 }
346
readKeywords(Binary::const_iterator iterator)347 kwdVpdMap memoryVpdParser::readKeywords(Binary::const_iterator iterator)
348 {
349 KeywordVpdMap map{};
350
351 // collect Dimm size value
352 auto dimmSize = getDDimmSize(iterator);
353 if (!dimmSize)
354 {
355 cerr << "Error: Calculated dimm size is 0.";
356 }
357
358 map.emplace("MemorySizeInKB", dimmSize);
359 // point the iterator to DIMM data and skip "11S"
360 advance(iterator, MEMORY_VPD_DATA_START + 3);
361 Binary partNumber(iterator, iterator + PART_NUM_LEN);
362
363 advance(iterator, PART_NUM_LEN);
364 Binary serialNumber(iterator, iterator + SERIAL_NUM_LEN);
365
366 advance(iterator, SERIAL_NUM_LEN);
367 Binary ccin(iterator, iterator + CCIN_LEN);
368
369 Binary mfgId(DRAM_MANUFACTURER_ID_LENGTH);
370 std::copy_n((memVpd.cbegin() + DRAM_MANUFACTURER_ID_OFFSET),
371 DRAM_MANUFACTURER_ID_LENGTH, mfgId.begin());
372
373 map.emplace("FN", partNumber);
374 map.emplace("PN", move(partNumber));
375 map.emplace("SN", move(serialNumber));
376 map.emplace("CC", move(ccin));
377 map.emplace("DI", move(mfgId));
378
379 return map;
380 }
381
parse()382 variant<kwdVpdMap, Store> memoryVpdParser::parse()
383 {
384 // Read the data and return the map
385 auto iterator = memVpd.cbegin();
386 auto vpdDataMap = readKeywords(iterator);
387
388 return vpdDataMap;
389 }
390
getInterfaceName() const391 std::string memoryVpdParser::getInterfaceName() const
392 {
393 return memVpdInf;
394 }
395
396 } // namespace parser
397 } // namespace memory
398 } // namespace vpd
399 } // namespace openpower
400