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