xref: /openbmc/openpower-vpd-parser/vpd-manager/src/isdimm_parser.cpp (revision e606f0611e0dba5243fd07cc82db23c31bcb1905)
1 #include "isdimm_parser.hpp"
2 
3 #include "constants.hpp"
4 #include "logger.hpp"
5 
6 #include <algorithm>
7 #include <iostream>
8 #include <numeric>
9 #include <optional>
10 #include <string>
11 #include <unordered_map>
12 
13 namespace vpd
14 {
15 
16 // Constants
17 constexpr auto SPD_JEDEC_DDR4_SDRAM_CAP_MASK = 0x0F;
18 constexpr auto SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MASK = 0x07;
19 constexpr auto SPD_JEDEC_DDR4_SDRAM_WIDTH_MASK = 0x07;
20 constexpr auto SPD_JEDEC_DDR4_NUM_RANKS_MASK = 0x38;
21 constexpr auto SPD_JEDEC_DDR4_DIE_COUNT_MASK = 0x70;
22 constexpr auto SPD_JEDEC_DDR4_SINGLE_LOAD_STACK = 0x02;
23 constexpr auto SPD_JEDEC_DDR4_SIGNAL_LOADING_MASK = 0x03;
24 
25 constexpr auto SPD_JEDEC_DDR4_SDRAMCAP_MULTIPLIER = 256;
26 constexpr auto SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER = 8;
27 constexpr auto SPD_JEDEC_DDR4_SDRAM_WIDTH_MULTIPLIER = 4;
28 constexpr auto SPD_JEDEC_DDR4_SDRAMCAP_RESERVED = 8;
29 constexpr auto SPD_JEDEC_DDR4_4_RESERVED_BITS = 4;
30 constexpr auto SPD_JEDEC_DDR4_3_RESERVED_BITS = 3;
31 constexpr auto SPD_JEDEC_DDR4_DIE_COUNT_RIGHT_SHIFT = 4;
32 
33 constexpr auto SPD_JEDEC_DDR4_MFG_ID_MSB_OFFSET = 321;
34 constexpr auto SPD_JEDEC_DDR4_MFG_ID_LSB_OFFSET = 320;
35 constexpr auto SPD_JEDEC_DDR4_SN_BYTE0_OFFSET = 325;
36 constexpr auto SPD_JEDEC_DDR4_SN_BYTE1_OFFSET = 326;
37 constexpr auto SPD_JEDEC_DDR4_SN_BYTE2_OFFSET = 327;
38 constexpr auto SPD_JEDEC_DDR4_SN_BYTE3_OFFSET = 328;
39 constexpr auto SPD_JEDEC_DDR4_SDRAM_DENSITY_BANK_OFFSET = 4;
40 constexpr auto SPD_JEDEC_DDR4_SDRAM_ADDR_OFFSET = 5;
41 constexpr auto SPD_JEDEC_DDR4_DRAM_PRI_PACKAGE_OFFSET = 6;
42 constexpr auto SPD_JEDEC_DDR4_DRAM_MODULE_ORG_OFFSET = 12;
43 static constexpr auto SPD_JEDEC_DDR4_DRAM_MANUFACTURER_ID_OFFSET = 320;
44 static constexpr auto SPD_JEDEC_DRAM_MANUFACTURER_ID_LENGTH = 2;
45 
46 // Lookup tables
47 const std::map<std::tuple<std::string, uint8_t>, std::string> pnFreqFnMap = {
48     {std::make_tuple("8421000", 6), "78P4191"},
49     {std::make_tuple("8421008", 6), "78P4192"},
50     {std::make_tuple("8529000", 6), "78P4197"},
51     {std::make_tuple("8529008", 6), "78P4198"},
52     {std::make_tuple("8529928", 6), "78P4199"},
53     {std::make_tuple("8529B28", 6), "78P4200"},
54     {std::make_tuple("8631928", 6), "78P6925"},
55     {std::make_tuple("8529000", 5), "78P7317"},
56     {std::make_tuple("8529008", 5), "78P7318"},
57     {std::make_tuple("8631008", 5), "78P6815"}};
58 
59 const std::unordered_map<std::string, std::string> pnCCINMap = {
60     {"78P4191", "324D"}, {"78P4192", "324E"}, {"78P4197", "324E"},
61     {"78P4198", "324F"}, {"78P4199", "325A"}, {"78P4200", "324C"},
62     {"78P6925", "32BC"}, {"78P7317", "331A"}, {"78P7318", "331F"},
63     {"78P6815", "32BB"}};
64 
getDDR4DimmCapacity(types::BinaryVector::const_iterator & i_iterator)65 auto JedecSpdParser::getDDR4DimmCapacity(
66     types::BinaryVector::const_iterator& i_iterator)
67 {
68     size_t l_tmp = 0, l_dimmSize = 0;
69 
70     size_t l_sdramCap = 1, l_priBusWid = 1, l_sdramWid = 1,
71            l_logicalRanksPerDimm = 1;
72     size_t l_dieCount = 1;
73 
74     // NOTE: This calculation is Only for DDR4
75 
76     // Calculate SDRAM  capacity
77     l_tmp = i_iterator[constants::SPD_BYTE_4] & SPD_JEDEC_DDR4_SDRAM_CAP_MASK;
78 
79     /* Make sure the bits are not Reserved */
80     if (l_tmp >= SPD_JEDEC_DDR4_SDRAMCAP_RESERVED)
81     {
82         logging::logMessage(
83             "Bad data in spd byte 4. Can't calculate SDRAM capacity "
84             "and so dimm size.\n ");
85         return l_dimmSize;
86     }
87     l_sdramCap = (l_sdramCap << l_tmp) * SPD_JEDEC_DDR4_SDRAMCAP_MULTIPLIER;
88 
89     /* Calculate Primary bus width */
90     l_tmp = i_iterator[constants::SPD_BYTE_13] &
91             SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MASK;
92     if (l_tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
93     {
94         logging::logMessage(
95             "Bad data in spd byte 13. Can't calculate primary bus "
96             "width and so dimm size.\n ");
97         return l_dimmSize;
98     }
99     l_priBusWid = (l_priBusWid << l_tmp) *
100                   SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER;
101 
102     /* Calculate SDRAM width */
103     l_tmp = i_iterator[constants::SPD_BYTE_12] &
104             SPD_JEDEC_DDR4_SDRAM_WIDTH_MASK;
105     if (l_tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
106     {
107         logging::logMessage(
108             "Bad data in spd byte 12. Can't calculate SDRAM width and "
109             "so dimm size.\n ");
110         return l_dimmSize;
111     }
112     l_sdramWid = (l_sdramWid << l_tmp) * SPD_JEDEC_DDR4_SDRAM_WIDTH_MULTIPLIER;
113 
114     l_tmp = i_iterator[constants::SPD_BYTE_6] &
115             SPD_JEDEC_DDR4_SIGNAL_LOADING_MASK;
116     if (l_tmp == SPD_JEDEC_DDR4_SINGLE_LOAD_STACK)
117     {
118         // Fetch die count
119         l_tmp = i_iterator[constants::SPD_BYTE_6] &
120                 SPD_JEDEC_DDR4_DIE_COUNT_MASK;
121         l_tmp >>= SPD_JEDEC_DDR4_DIE_COUNT_RIGHT_SHIFT;
122         l_dieCount = l_tmp + 1;
123     }
124 
125     /* Calculate Number of ranks */
126     l_tmp = i_iterator[constants::SPD_BYTE_12] & SPD_JEDEC_DDR4_NUM_RANKS_MASK;
127     l_tmp >>= SPD_JEDEC_DDR4_3_RESERVED_BITS;
128 
129     if (l_tmp >= SPD_JEDEC_DDR4_4_RESERVED_BITS)
130     {
131         logging::logMessage(
132             "Can't calculate number of ranks. Invalid data found.\n ");
133         return l_dimmSize;
134     }
135     l_logicalRanksPerDimm = (l_tmp + 1) * l_dieCount;
136 
137     l_dimmSize = (l_sdramCap / SPD_JEDEC_DDR4_PRI_BUS_WIDTH_MULTIPLIER) *
138                  (l_priBusWid / l_sdramWid) * l_logicalRanksPerDimm;
139 
140     return l_dimmSize;
141 }
142 
getDDR4PartNumber(types::BinaryVector::const_iterator & i_iterator)143 std::string_view JedecSpdParser::getDDR4PartNumber(
144     types::BinaryVector::const_iterator& i_iterator)
145 {
146     char l_tmpPN[constants::PART_NUM_LEN + 1] = {'\0'};
147     sprintf(l_tmpPN, "%02X%02X%02X%X",
148             i_iterator[SPD_JEDEC_DDR4_SDRAM_DENSITY_BANK_OFFSET],
149             i_iterator[SPD_JEDEC_DDR4_SDRAM_ADDR_OFFSET],
150             i_iterator[SPD_JEDEC_DDR4_DRAM_PRI_PACKAGE_OFFSET],
151             i_iterator[SPD_JEDEC_DDR4_DRAM_MODULE_ORG_OFFSET] & 0x0F);
152     std::string l_partNumber(l_tmpPN, sizeof(l_tmpPN) - 1);
153     return l_partNumber;
154 }
155 
getDDR4SerialNumber(types::BinaryVector::const_iterator & i_iterator)156 std::string JedecSpdParser::getDDR4SerialNumber(
157     types::BinaryVector::const_iterator& i_iterator)
158 {
159     char l_tmpSN[constants::SERIAL_NUM_LEN + 1] = {'\0'};
160     sprintf(l_tmpSN, "%02X%02X%02X%02X%02X%02X",
161             i_iterator[SPD_JEDEC_DDR4_MFG_ID_MSB_OFFSET],
162             i_iterator[SPD_JEDEC_DDR4_MFG_ID_LSB_OFFSET],
163             i_iterator[SPD_JEDEC_DDR4_SN_BYTE0_OFFSET],
164             i_iterator[SPD_JEDEC_DDR4_SN_BYTE1_OFFSET],
165             i_iterator[SPD_JEDEC_DDR4_SN_BYTE2_OFFSET],
166             i_iterator[SPD_JEDEC_DDR4_SN_BYTE3_OFFSET]);
167     std::string l_serialNumber(l_tmpSN, sizeof(l_tmpSN) - 1);
168     return l_serialNumber;
169 }
170 
getDDR4FruNumber(const std::string & i_partNumber,types::BinaryVector::const_iterator & i_iterator)171 std::string_view JedecSpdParser::getDDR4FruNumber(
172     const std::string& i_partNumber,
173     types::BinaryVector::const_iterator& i_iterator)
174 {
175     // check for 128GB ISRDIMM not implemented
176     //(128GB 2RX4(8GX72) IS RDIMM 36*(16GBIT, 2H),1.2V 288PIN,1.2" ROHS) - NA
177 
178     // MTB Units is used in deciding the frequency of the DIMM
179     // This is applicable only for DDR4 specification
180     // 10 - DDR4-1600
181     // 9  - DDR4-1866
182     // 8  - DDR4-2133
183     // 7  - DDR4-2400
184     // 6  - DDR4-2666
185     // 5  - DDR4-3200
186     // pnFreqFnMap < tuple <partNumber, MTBUnits>, fruNumber>
187     uint8_t l_mtbUnits = i_iterator[constants::SPD_BYTE_18] &
188                          constants::SPD_BYTE_MASK;
189     std::string l_fruNumber = "FFFFFFF";
190     auto it = pnFreqFnMap.find({i_partNumber, l_mtbUnits});
191     if (it != pnFreqFnMap.end())
192     {
193         l_fruNumber = it->second;
194     }
195 
196     return l_fruNumber;
197 }
198 
getDDR4CCIN(const std::string & i_fruNumber)199 std::string_view JedecSpdParser::getDDR4CCIN(const std::string& i_fruNumber)
200 {
201     auto it = pnCCINMap.find(i_fruNumber);
202     if (it != pnCCINMap.end())
203     {
204         return it->second;
205     }
206     return "XXXX"; // Return default value as XXXX
207 }
208 
getDDR4ManufacturerId()209 types::BinaryVector JedecSpdParser::getDDR4ManufacturerId()
210 {
211     types::BinaryVector l_mfgId(SPD_JEDEC_DRAM_MANUFACTURER_ID_LENGTH);
212 
213     if (m_memSpd.size() < (SPD_JEDEC_DDR4_DRAM_MANUFACTURER_ID_OFFSET +
214                            SPD_JEDEC_DRAM_MANUFACTURER_ID_LENGTH))
215     {
216         logging::logMessage(
217             "VPD length is less than the offset of Manufacturer ID. Can't fetch it");
218         return l_mfgId;
219     }
220 
221     std::copy_n((m_memSpd.cbegin() +
222                  SPD_JEDEC_DDR4_DRAM_MANUFACTURER_ID_OFFSET),
223                 SPD_JEDEC_DRAM_MANUFACTURER_ID_LENGTH, l_mfgId.begin());
224     return l_mfgId;
225 }
226 
getDDR5DimmCapacity(types::BinaryVector::const_iterator & i_iterator)227 auto JedecSpdParser::getDDR5DimmCapacity(
228     types::BinaryVector::const_iterator& i_iterator)
229 {
230     // dummy implementation to be updated when required
231     size_t dimmSize = 0;
232     (void)i_iterator;
233     return dimmSize;
234 }
235 
getDDR5PartNumber(types::BinaryVector::const_iterator & i_iterator)236 auto JedecSpdParser::getDDR5PartNumber(
237     types::BinaryVector::const_iterator& i_iterator)
238 {
239     // dummy implementation to be updated when required
240     std::string l_partNumber;
241     (void)i_iterator;
242     l_partNumber = "0123456";
243     return l_partNumber;
244 }
245 
getDDR5SerialNumber(types::BinaryVector::const_iterator & i_iterator)246 auto JedecSpdParser::getDDR5SerialNumber(
247     types::BinaryVector::const_iterator& i_iterator)
248 {
249     // dummy implementation to be updated when required
250     std::string l_serialNumber;
251     (void)i_iterator;
252     l_serialNumber = "444444444444";
253     return l_serialNumber;
254 }
255 
getDDR5FruNumber(const std::string & i_partNumber)256 auto JedecSpdParser::getDDR5FruNumber(const std::string& i_partNumber)
257 {
258     // dummy implementation to be updated when required
259     static std::unordered_map<std::string, std::string> pnFruMap = {
260         {"1234567", "XXXXXXX"}};
261 
262     std::string l_fruNumber;
263     auto itr = pnFruMap.find(i_partNumber);
264     if (itr != pnFruMap.end())
265     {
266         l_fruNumber = itr->second;
267     }
268     else
269     {
270         l_fruNumber = "FFFFFFF";
271     }
272     return l_fruNumber;
273 }
274 
getDDR5CCIN(const std::string & i_partNumber)275 auto JedecSpdParser::getDDR5CCIN(const std::string& i_partNumber)
276 {
277     // dummy implementation to be updated when required
278     static std::unordered_map<std::string, std::string> pnCCINMap = {
279         {"1234567", "XXXX"}};
280 
281     std::string ccin = "XXXX";
282     auto itr = pnCCINMap.find(i_partNumber);
283     if (itr != pnCCINMap.end())
284     {
285         ccin = itr->second;
286     }
287     return ccin;
288 }
289 
readKeywords(types::BinaryVector::const_iterator & i_iterator)290 types::JedecSpdMap JedecSpdParser::readKeywords(
291     types::BinaryVector::const_iterator& i_iterator)
292 {
293     types::JedecSpdMap l_keywordValueMap{};
294     size_t dimmSize = getDDR4DimmCapacity(i_iterator);
295     if (!dimmSize)
296     {
297         logging::logMessage("Error: Calculated dimm size is 0.");
298     }
299     else
300     {
301         l_keywordValueMap.emplace("MemorySizeInKB",
302                                   dimmSize * constants::CONVERT_MB_TO_KB);
303     }
304 
305     auto l_partNumber = getDDR4PartNumber(i_iterator);
306     auto l_fruNumber = getDDR4FruNumber(
307         std::string(l_partNumber.begin(), l_partNumber.end()), i_iterator);
308     auto l_serialNumber = getDDR4SerialNumber(i_iterator);
309     auto ccin =
310         getDDR4CCIN(std::string(l_fruNumber.begin(), l_fruNumber.end()));
311     // PN value is made same as FN value
312     auto l_displayPartNumber = l_fruNumber;
313     auto l_mfgId = getDDR4ManufacturerId();
314 
315     l_keywordValueMap.emplace("PN",
316                               move(std::string(l_displayPartNumber.begin(),
317                                                l_displayPartNumber.end())));
318     l_keywordValueMap.emplace(
319         "FN", move(std::string(l_fruNumber.begin(), l_fruNumber.end())));
320     l_keywordValueMap.emplace("SN", move(l_serialNumber));
321     l_keywordValueMap.emplace("CC",
322                               move(std::string(ccin.begin(), ccin.end())));
323     l_keywordValueMap.emplace("DI", move(l_mfgId));
324 
325     return l_keywordValueMap;
326 }
327 
parse()328 types::VPDMapVariant JedecSpdParser::parse()
329 {
330     // Read the data and return the map
331     auto l_iterator = m_memSpd.cbegin();
332     auto l_spdDataMap = readKeywords(l_iterator);
333     return l_spdDataMap;
334 }
335 
336 } // namespace vpd
337