blob: 3472f42df010a3feca3a534f0f89e4b44e073ba0 [file] [log] [blame]
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05001#include <algorithm>
2#include <map>
3#include <numeric>
4#include "ipmi_fru_info_area.hpp"
5#include <phosphor-logging/elog.hpp>
6namespace ipmi
7{
8namespace fru
9{
10using namespace phosphor::logging;
11
12//Property variables
13static constexpr auto partNumber = "PartNumber";
14static constexpr auto serialNumber = "SerialNumber";
15static constexpr auto manufacturer = "Manufacturer";
16static constexpr auto buildDate = "BuildDate";
17static constexpr auto model = "Model";
18static constexpr auto prettyName = "PrettyName";
19static constexpr auto version = "Version";
20
21//Board info areas
22static constexpr auto board = "Board";
23static constexpr auto chassis = "Chassis";
24static constexpr auto product = "Product";
25
26static constexpr auto specVersion = 0x1;
27static constexpr auto recordUnitOfMeasurment = 0x8; //size in bytes
28static constexpr auto checksumSize = 0x1; //size in bytes
29static constexpr auto recordNotPresent = 0x0;
30static constexpr auto englishLanguageCode = 0x0;
31static constexpr auto typeLengthByteNull = 0x0;
32static constexpr auto endOfCustomFields = 0xC1;
33static constexpr auto commonHeaderFormatSize = 0x8; //size in bytes
34static constexpr auto manufacturingDateSize = 0x3;
35static constexpr auto areaSizeOffset = 0x1;
36
37/**
38 * @brief Format Beginning of Individual IPMI FRU Data Section
39 *
40 * @param[in] langCode Language code
41 * @param[in/out] data FRU area data
42 */
43void preFormatProcessing(bool langCode, FruAreaData& data)
44{
45 //Add id for version of FRU Info Storage Spec used
46 data.emplace_back(specVersion);
47
48 //Add Data Size - 0 as a placeholder, can edit after the data is finalized
49 data.emplace_back(typeLengthByteNull);
50
51 if (langCode)
52 {
53 data.emplace_back(englishLanguageCode);
54 }
55}
56
57/**
58 * @brief Append checksum of the FRU area data
59 *
60 * @param[in/out] data FRU area data
61 */
62void appendDataChecksum(FruAreaData& data)
63{
64 uint8_t checksumVal = std::accumulate(data.begin(), data.end(), 0);
65 // Push the Zero checksum as the last byte of this data
66 // This appears to be a simple summation of all the bytes
67 data.emplace_back(-checksumVal);
68}
69
70/**
71 * @brief Append padding bytes for the FRU area data
72 *
73 * @param[in/out] data FRU area data
74 */
75void padData(FruAreaData& data)
76{
77 uint8_t pad = (data.size() + checksumSize) % recordUnitOfMeasurment;
78 if (pad)
79 {
80 data.resize((data.size() + recordUnitOfMeasurment - pad));
81 }
82}
83
84/**
85 * @brief Format End of Individual IPMI FRU Data Section
86 *
87 * @param[in/out] fruAreaData FRU area info data
88 */
89void postFormatProcessing(FruAreaData& data)
90{
91 //This area needs to be padded to a multiple of 8 bytes (after checksum)
92 padData(data);
93
94 //Set size of data info area
95 data.at(areaSizeOffset) = (data.size() + checksumSize) /
96 (recordUnitOfMeasurment);
97
98 //Finally add board info checksum
99 appendDataChecksum(data);
100}
101
102/**
103 * @brief Read property value from inventory and append to the FRU area data
104 *
105 * @param[in] key key to search for in the property inventory data
106 * @param[in] propMap map of property values
107 * @param[in,out] data FRU area data to be appended
108 */
109void appendData(const Property& key, const PropertyMap& propMap,
110 FruAreaData& data)
111{
112 auto iter = propMap.find(key);
113 if (iter != propMap.end())
114 {
115 auto value = iter->second;
116 //If starts with 0x or 0X remove them
117 //ex: 0x123a just take 123a
118 if ((value.compare(0, 2, "0x")) == 0 ||
119 (value.compare(0, 2, "0X") == 0))
120 {
121 value.erase(0, 2);
122 }
123 data.emplace_back(value.length());
124 std::copy(value.begin(), value.end(), std::back_inserter(data));
125 }
126 else
127 {
128 //set 0 size
129 data.emplace_back(typeLengthByteNull);
130 }
131}
132
133
134/**
135 * @brief Appends Build Date
136 *
137 * @param[in] propMap map of property values
138 * @param[in/out] data FRU area to add the manfufacture date
139 */
140void appendMfgDate(const PropertyMap& propMap, FruAreaData& data)
141{
142 //MFG Date/Time
143 auto iter = propMap.find(buildDate);
144 if (iter != propMap.end())
145 {
146 auto& value = iter->second;
147 if (value.length() == manufacturingDateSize)
148 {
149 std::copy(
150 value.begin(), value.end(), std::back_inserter(data));
151 return;
152 }
153 }
154 //Blank date
155 data.emplace_back(0);
156 data.emplace_back(0);
157 data.emplace_back(0);
158}
159
160/**
161 * @brief Builds a section of the common header
162 *
163 * @param[in] infoAreaSize size of the FRU area to write
164 * @param[in] offset Current offset for data in overall record
165 * @param[in/out] data Common Header section data container
166 */
167void buildCommonHeaderSection(
168 const uint32_t& infoAreaSize, uint32_t& offset, FruAreaData& data)
169{
170 //Check if data for internal use section populated
171 if (infoAreaSize == 0)
172 {
173 //Indicate record not present
174 data.emplace_back(recordNotPresent);
175 }
176 else
177 {
178 //Place data to define offset to area data section
179 data.emplace_back((offset + commonHeaderFormatSize)
180 / recordUnitOfMeasurment);
181 offset += infoAreaSize;
182 }
183}
184
185/**
186 * @brief Builds the Chassis info area data section
187 *
188 * @param[in] propMap map of properties for chassis info area
189 * @return FruAreaData container with chassis info area
190 */
191FruAreaData buildChassisInfoArea(const PropertyMap& propMap)
192{
193 FruAreaData fruAreaData;
194 if (!propMap.empty())
195 {
196 //Set formatting data that goes at the beginning of the record
197 preFormatProcessing(false, fruAreaData);
198
199 //chassis type
200 fruAreaData.emplace_back(0);
201
202 //Chasiss part number, in config.yaml it is configured as model
203 appendData(model, propMap, fruAreaData);
204
205 //Board serial number
206 appendData(serialNumber, propMap, fruAreaData);
207
208 //Indicate End of Custom Fields
209 fruAreaData.emplace_back(endOfCustomFields);
210
211 //Complete record data formatting
212 postFormatProcessing(fruAreaData);
213 }
214 return fruAreaData;
215}
216
217/**
218 * @brief Builds the Board info area data section
219 *
220 * @param[in] propMap map of properties for board info area
221 * @return FruAreaData container with board info area
222 */
223FruAreaData buildBoardInfoArea(const PropertyMap& propMap)
224{
225 FruAreaData fruAreaData;
226 if (!propMap.empty())
227 {
228 preFormatProcessing(true, fruAreaData);
229
230 //Manufacturing date
231 appendMfgDate(propMap, fruAreaData);
232
233 //manufacturer
234 appendData(manufacturer, propMap, fruAreaData);
235
236 //Product name/Pretty name
237 appendData(prettyName, propMap, fruAreaData);
238
239 //Board serial number
240 appendData(serialNumber, propMap, fruAreaData);
241
242 //Board part number
243 appendData(partNumber, propMap, fruAreaData);
244
245 //FRU File ID - Empty
246 fruAreaData.emplace_back(typeLengthByteNull);
247
248 // Empty FRU File ID bytes
249 fruAreaData.emplace_back(recordNotPresent);
250
251 //End of custom fields
252 fruAreaData.emplace_back(endOfCustomFields);
253
254 postFormatProcessing(fruAreaData);
255 }
256 return fruAreaData;
257}
258
259/**
260 * @brief Builds the Product info area data section
261 *
262 * @param[in] propMap map of FRU properties for Board info area
263 * @return FruAreaData container with product info area data
264 */
265FruAreaData buildProductInfoArea(const PropertyMap& propMap)
266{
267 FruAreaData fruAreaData;
268 if (!propMap.empty())
269 {
270 //Set formatting data that goes at the beginning of the record
271 preFormatProcessing(true, fruAreaData);
272
273 //manufacturer
274 appendData(manufacturer, propMap, fruAreaData);
275
276 //Product name/Pretty name
277 appendData(prettyName, propMap, fruAreaData);
278
279 //Product part/model number
280 appendData(model, propMap, fruAreaData);
281
282 //Product version
283 appendData(version, propMap, fruAreaData);
284
285 //Serial Number
286 appendData(serialNumber, propMap, fruAreaData);
287
288 //Add Asset Tag
289 fruAreaData.emplace_back(recordNotPresent);
290
291 //FRU File ID - Empty
292 fruAreaData.emplace_back(typeLengthByteNull);
293
294 // Empty FRU File ID bytes
295 fruAreaData.emplace_back(recordNotPresent);
296
297 //End of custom fields
298 fruAreaData.emplace_back(endOfCustomFields);
299
300 postFormatProcessing(fruAreaData);
301 }
302 return fruAreaData;
303}
304
305FruAreaData buildFruAreaData(const FruInventoryData& inventory)
306{
307 FruAreaData combFruArea;
308 //Now build common header with data for this FRU Inv Record
309 //Use this variable to increment size of header as we go along to determine
310 //offset for the subsequent area offsets
311 uint32_t curDataOffset = 0;
312
313 //First byte is id for version of FRU Info Storage Spec used
314 combFruArea.emplace_back(specVersion);
315
316 //2nd byte is offset to internal use data
317 combFruArea.emplace_back(recordNotPresent);
318
319 //3rd byte is offset to chassis data
320 FruAreaData chassisArea;
321 auto chassisIt = inventory.find(chassis);
322 if (chassisIt != inventory.end())
323 {
324 chassisArea = std::move(buildChassisInfoArea(chassisIt->second));
325 }
326 buildCommonHeaderSection(chassisArea.size(), curDataOffset, combFruArea);
327
328 //4th byte is offset to board data
329 FruAreaData boardArea;
330 auto boardIt = inventory.find(board);
331 if (boardIt != inventory.end())
332 {
333 boardArea = std::move(buildBoardInfoArea(boardIt->second));
334 }
335
336 //5th byte is offset to product data
337 FruAreaData prodArea;
338 auto prodIt = inventory.find(product);
339 if (prodIt != inventory.end())
340 {
341 prodArea = std::move(buildProductInfoArea(prodIt->second));
342 }
343 buildCommonHeaderSection(prodArea.size(), curDataOffset, combFruArea);
344
345 //6th byte is offset to multirecord data
346 combFruArea.emplace_back(recordNotPresent);
347
348 //7th byte is PAD
349 padData(combFruArea);
350
351 //8th (Final byte of Header Format) is the checksum
352 appendDataChecksum(combFruArea);
353
354 //Combine everything into one full IPMI FRU specification Record
355 //add chassis use area data
356 combFruArea.insert(
357 combFruArea.end(), chassisArea.begin(), chassisArea.end());
358
359 //add board area data
360 combFruArea.insert(combFruArea.end(), boardArea.begin(), boardArea.end());
361
362 //add product use area data
363 combFruArea.insert(combFruArea.end(), prodArea.begin(), prodArea.end());
364
365 return combFruArea;
366}
367
368} //fru
369} //ipmi