blob: 1951f8c2c51989a53b6c0bbd05232321231f4a5f [file] [log] [blame]
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05001#include <algorithm>
2#include <map>
3#include <numeric>
Andres Oportus7ebd2462018-04-09 10:35:21 -07004
5#include <ctime>
6
Marri Devender Rao7d9157e2017-07-01 16:11:40 -05007#include "ipmi_fru_info_area.hpp"
8#include <phosphor-logging/elog.hpp>
9namespace ipmi
10{
11namespace fru
12{
13using namespace phosphor::logging;
14
15//Property variables
16static constexpr auto partNumber = "PartNumber";
17static constexpr auto serialNumber = "SerialNumber";
18static constexpr auto manufacturer = "Manufacturer";
19static constexpr auto buildDate = "BuildDate";
20static constexpr auto model = "Model";
21static constexpr auto prettyName = "PrettyName";
22static constexpr auto version = "Version";
23
24//Board info areas
25static constexpr auto board = "Board";
26static constexpr auto chassis = "Chassis";
27static constexpr auto product = "Product";
28
29static constexpr auto specVersion = 0x1;
Ratan Gupta3e6a7692018-02-07 16:10:16 +053030static constexpr auto recordUnitOfMeasurement = 0x8; //size in bytes
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050031static constexpr auto checksumSize = 0x1; //size in bytes
32static constexpr auto recordNotPresent = 0x0;
33static constexpr auto englishLanguageCode = 0x0;
34static constexpr auto typeLengthByteNull = 0x0;
35static constexpr auto endOfCustomFields = 0xC1;
36static constexpr auto commonHeaderFormatSize = 0x8; //size in bytes
37static constexpr auto manufacturingDateSize = 0x3;
38static constexpr auto areaSizeOffset = 0x1;
Ratan Gupta6edfc5c2018-01-31 21:41:45 +053039static constexpr uint8_t typeASCII = 0xC0;
40static constexpr auto maxRecordAttributeValue = 0x1F;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050041
Andres Oportus7ebd2462018-04-09 10:35:21 -070042static constexpr auto secs_from_1970_1996 = 820454400;
43static constexpr auto secs_per_min = 60;
44
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050045/**
46 * @brief Format Beginning of Individual IPMI FRU Data Section
47 *
48 * @param[in] langCode Language code
49 * @param[in/out] data FRU area data
50 */
51void preFormatProcessing(bool langCode, FruAreaData& data)
52{
53 //Add id for version of FRU Info Storage Spec used
54 data.emplace_back(specVersion);
55
56 //Add Data Size - 0 as a placeholder, can edit after the data is finalized
57 data.emplace_back(typeLengthByteNull);
58
59 if (langCode)
60 {
61 data.emplace_back(englishLanguageCode);
62 }
63}
64
65/**
66 * @brief Append checksum of the FRU area data
67 *
68 * @param[in/out] data FRU area data
69 */
70void appendDataChecksum(FruAreaData& data)
71{
72 uint8_t checksumVal = std::accumulate(data.begin(), data.end(), 0);
73 // Push the Zero checksum as the last byte of this data
74 // This appears to be a simple summation of all the bytes
75 data.emplace_back(-checksumVal);
76}
77
78/**
79 * @brief Append padding bytes for the FRU area data
80 *
81 * @param[in/out] data FRU area data
82 */
83void padData(FruAreaData& data)
84{
Ratan Gupta3e6a7692018-02-07 16:10:16 +053085 uint8_t pad = (data.size() + checksumSize) % recordUnitOfMeasurement;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050086 if (pad)
87 {
Ratan Gupta3e6a7692018-02-07 16:10:16 +053088 data.resize((data.size() + recordUnitOfMeasurement - pad));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -050089 }
90}
91
92/**
93 * @brief Format End of Individual IPMI FRU Data Section
94 *
95 * @param[in/out] fruAreaData FRU area info data
96 */
97void postFormatProcessing(FruAreaData& data)
98{
99 //This area needs to be padded to a multiple of 8 bytes (after checksum)
100 padData(data);
101
102 //Set size of data info area
103 data.at(areaSizeOffset) = (data.size() + checksumSize) /
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530104 (recordUnitOfMeasurement);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500105
Ratan Gupta2f66f002018-01-31 21:26:25 +0530106 //Finally add area checksum
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500107 appendDataChecksum(data);
108}
109
110/**
111 * @brief Read property value from inventory and append to the FRU area data
112 *
113 * @param[in] key key to search for in the property inventory data
114 * @param[in] propMap map of property values
115 * @param[in,out] data FRU area data to be appended
116 */
117void appendData(const Property& key, const PropertyMap& propMap,
118 FruAreaData& data)
119{
120 auto iter = propMap.find(key);
121 if (iter != propMap.end())
122 {
123 auto value = iter->second;
124 //If starts with 0x or 0X remove them
125 //ex: 0x123a just take 123a
126 if ((value.compare(0, 2, "0x")) == 0 ||
127 (value.compare(0, 2, "0X") == 0))
128 {
129 value.erase(0, 2);
130 }
Ratan Gupta6edfc5c2018-01-31 21:41:45 +0530131
132 // 5 bits for length
133 // if length is greater then 31(2^5) bytes then trim the data to 31 bytess.
134 auto valueLength = (value.length() > maxRecordAttributeValue) ?
135 maxRecordAttributeValue : value.length();
136 // 2 bits for type
137 // Set the type to ascii
138 uint8_t typeLength = valueLength | ipmi::fru::typeASCII;
139
140 data.emplace_back(typeLength);
141 std::copy(value.begin(),
142 value.begin() + valueLength,
143 std::back_inserter(data));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500144 }
145 else
146 {
147 //set 0 size
148 data.emplace_back(typeLengthByteNull);
149 }
150}
151
152
153/**
154 * @brief Appends Build Date
155 *
156 * @param[in] propMap map of property values
157 * @param[in/out] data FRU area to add the manfufacture date
158 */
159void appendMfgDate(const PropertyMap& propMap, FruAreaData& data)
160{
161 //MFG Date/Time
162 auto iter = propMap.find(buildDate);
163 if (iter != propMap.end())
164 {
Andres Oportus7ebd2462018-04-09 10:35:21 -0700165 tm time = {};
166 strptime(iter->second.c_str(), "%F - %H:%M:%S", &time);
167 time_t raw = mktime(&time);
168
169 // From FRU Spec:
170 // "Mfg. Date / Time
171 // Number of minutes from 0:00 hrs 1/1/96.
172 // LSbyte first (little endian)
173 // 00_00_00h = unspecified."
174 if (raw > secs_from_1970_1996)
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500175 {
Andres Oportus7ebd2462018-04-09 10:35:21 -0700176 raw -= secs_from_1970_1996;
177 raw /= secs_per_min;
178 uint8_t fru_raw[3];
179 fru_raw[0] = raw & 0xFF;
180 fru_raw[1] = (raw >> 8) & 0xFF;
181 fru_raw[2] = (raw >> 16) & 0xFF;
182 std::copy(fru_raw, fru_raw + 3, std::back_inserter(data));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500183 return;
184 }
Andres Oportus7ebd2462018-04-09 10:35:21 -0700185 fprintf(stderr, "MgfDate invalid date: %u secs since UNIX epoch\n",
186 static_cast<unsigned int>(raw));
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500187 }
188 //Blank date
189 data.emplace_back(0);
190 data.emplace_back(0);
191 data.emplace_back(0);
192}
193
194/**
195 * @brief Builds a section of the common header
196 *
197 * @param[in] infoAreaSize size of the FRU area to write
198 * @param[in] offset Current offset for data in overall record
199 * @param[in/out] data Common Header section data container
200 */
201void buildCommonHeaderSection(
Ratan Gupta2f66f002018-01-31 21:26:25 +0530202 const uint32_t& infoAreaSize, uint16_t& offset, FruAreaData& data)
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500203{
204 //Check if data for internal use section populated
205 if (infoAreaSize == 0)
206 {
207 //Indicate record not present
208 data.emplace_back(recordNotPresent);
209 }
210 else
211 {
Ratan Gupta2f66f002018-01-31 21:26:25 +0530212 // offset should be multiple of 8.
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530213 auto remainder = offset % recordUnitOfMeasurement;
Ratan Gupta2f66f002018-01-31 21:26:25 +0530214 // add the padding bytes in the offset so that offset
215 // will be multiple of 8 byte.
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530216 offset += (remainder > 0) ? recordUnitOfMeasurement - remainder : 0;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500217 //Place data to define offset to area data section
Ratan Gupta3e6a7692018-02-07 16:10:16 +0530218 data.emplace_back(offset / recordUnitOfMeasurement);
Ratan Gupta2f66f002018-01-31 21:26:25 +0530219
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500220 offset += infoAreaSize;
221 }
222}
223
224/**
225 * @brief Builds the Chassis info area data section
226 *
227 * @param[in] propMap map of properties for chassis info area
228 * @return FruAreaData container with chassis info area
229 */
230FruAreaData buildChassisInfoArea(const PropertyMap& propMap)
231{
232 FruAreaData fruAreaData;
233 if (!propMap.empty())
234 {
235 //Set formatting data that goes at the beginning of the record
236 preFormatProcessing(false, fruAreaData);
237
238 //chassis type
239 fruAreaData.emplace_back(0);
240
241 //Chasiss part number, in config.yaml it is configured as model
242 appendData(model, propMap, fruAreaData);
243
244 //Board serial number
245 appendData(serialNumber, propMap, fruAreaData);
246
247 //Indicate End of Custom Fields
248 fruAreaData.emplace_back(endOfCustomFields);
249
250 //Complete record data formatting
251 postFormatProcessing(fruAreaData);
252 }
253 return fruAreaData;
254}
255
256/**
257 * @brief Builds the Board info area data section
258 *
259 * @param[in] propMap map of properties for board info area
260 * @return FruAreaData container with board info area
261 */
262FruAreaData buildBoardInfoArea(const PropertyMap& propMap)
263{
264 FruAreaData fruAreaData;
265 if (!propMap.empty())
266 {
267 preFormatProcessing(true, fruAreaData);
268
269 //Manufacturing date
270 appendMfgDate(propMap, fruAreaData);
271
272 //manufacturer
273 appendData(manufacturer, propMap, fruAreaData);
274
275 //Product name/Pretty name
276 appendData(prettyName, propMap, fruAreaData);
277
278 //Board serial number
279 appendData(serialNumber, propMap, fruAreaData);
280
281 //Board part number
282 appendData(partNumber, propMap, fruAreaData);
283
284 //FRU File ID - Empty
285 fruAreaData.emplace_back(typeLengthByteNull);
286
287 // Empty FRU File ID bytes
288 fruAreaData.emplace_back(recordNotPresent);
289
290 //End of custom fields
291 fruAreaData.emplace_back(endOfCustomFields);
292
293 postFormatProcessing(fruAreaData);
294 }
295 return fruAreaData;
296}
297
298/**
299 * @brief Builds the Product info area data section
300 *
301 * @param[in] propMap map of FRU properties for Board info area
302 * @return FruAreaData container with product info area data
303 */
304FruAreaData buildProductInfoArea(const PropertyMap& propMap)
305{
306 FruAreaData fruAreaData;
307 if (!propMap.empty())
308 {
309 //Set formatting data that goes at the beginning of the record
310 preFormatProcessing(true, fruAreaData);
311
312 //manufacturer
313 appendData(manufacturer, propMap, fruAreaData);
314
315 //Product name/Pretty name
316 appendData(prettyName, propMap, fruAreaData);
317
318 //Product part/model number
319 appendData(model, propMap, fruAreaData);
320
321 //Product version
322 appendData(version, propMap, fruAreaData);
323
324 //Serial Number
325 appendData(serialNumber, propMap, fruAreaData);
326
327 //Add Asset Tag
328 fruAreaData.emplace_back(recordNotPresent);
329
330 //FRU File ID - Empty
331 fruAreaData.emplace_back(typeLengthByteNull);
332
333 // Empty FRU File ID bytes
334 fruAreaData.emplace_back(recordNotPresent);
335
336 //End of custom fields
337 fruAreaData.emplace_back(endOfCustomFields);
338
339 postFormatProcessing(fruAreaData);
340 }
341 return fruAreaData;
342}
343
344FruAreaData buildFruAreaData(const FruInventoryData& inventory)
345{
Ratan Gupta2f66f002018-01-31 21:26:25 +0530346 FruAreaData combFruArea{};
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500347 //Now build common header with data for this FRU Inv Record
348 //Use this variable to increment size of header as we go along to determine
349 //offset for the subsequent area offsets
Ratan Gupta2f66f002018-01-31 21:26:25 +0530350 uint16_t curDataOffset = commonHeaderFormatSize;
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500351 //First byte is id for version of FRU Info Storage Spec used
352 combFruArea.emplace_back(specVersion);
353
354 //2nd byte is offset to internal use data
355 combFruArea.emplace_back(recordNotPresent);
356
357 //3rd byte is offset to chassis data
358 FruAreaData chassisArea;
359 auto chassisIt = inventory.find(chassis);
360 if (chassisIt != inventory.end())
361 {
362 chassisArea = std::move(buildChassisInfoArea(chassisIt->second));
363 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530364 // update the offset to chassis data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500365 buildCommonHeaderSection(chassisArea.size(), curDataOffset, combFruArea);
366
367 //4th byte is offset to board data
368 FruAreaData boardArea;
369 auto boardIt = inventory.find(board);
370 if (boardIt != inventory.end())
371 {
372 boardArea = std::move(buildBoardInfoArea(boardIt->second));
373 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530374 // update the offset to the board data.
375 buildCommonHeaderSection(boardArea.size(), curDataOffset, combFruArea);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500376
377 //5th byte is offset to product data
378 FruAreaData prodArea;
379 auto prodIt = inventory.find(product);
380 if (prodIt != inventory.end())
381 {
382 prodArea = std::move(buildProductInfoArea(prodIt->second));
383 }
Ratan Gupta2f66f002018-01-31 21:26:25 +0530384 // update the offset to the product data.
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500385 buildCommonHeaderSection(prodArea.size(), curDataOffset, combFruArea);
386
387 //6th byte is offset to multirecord data
388 combFruArea.emplace_back(recordNotPresent);
389
390 //7th byte is PAD
Ratan Gupta2f66f002018-01-31 21:26:25 +0530391 combFruArea.emplace_back(recordNotPresent);
Marri Devender Rao7d9157e2017-07-01 16:11:40 -0500392
393 //8th (Final byte of Header Format) is the checksum
394 appendDataChecksum(combFruArea);
395
396 //Combine everything into one full IPMI FRU specification Record
397 //add chassis use area data
398 combFruArea.insert(
399 combFruArea.end(), chassisArea.begin(), chassisArea.end());
400
401 //add board area data
402 combFruArea.insert(combFruArea.end(), boardArea.begin(), boardArea.end());
403
404 //add product use area data
405 combFruArea.insert(combFruArea.end(), prodArea.begin(), prodArea.end());
406
407 return combFruArea;
408}
409
410} //fru
411} //ipmi