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