blob: 3e09bc8740f42cf2254949e5bc51d075ec401acf [file] [log] [blame]
Vishwa4be4b7a2015-10-31 22:55:50 -05001#include <vector>
2#include <stdlib.h>
3#include <dlfcn.h>
4#include <errno.h>
5#include <stdio.h>
Vishwa4be4b7a2015-10-31 22:55:50 -05006#include <systemd/sd-bus.h>
Chris Austenb45c4cb2015-11-01 06:34:56 -06007#include <unistd.h>
vishwa13555bd2015-11-10 12:10:38 -06008#include <host-ipmid/ipmid-api.h>
vishwac93d6d42015-12-16 11:55:16 -06009#include <iostream>
10#include <memory>
11#include <algorithm>
12#include <fstream>
Yi Li75c2d462016-04-11 16:57:46 +080013#include <sstream>
Brad Bishopde6a3792016-07-25 17:09:12 -040014#include <mapper.h>
vishwac93d6d42015-12-16 11:55:16 -060015#include "frup.h"
Matthew Barth155c34f2016-10-18 14:33:17 -050016#include "fru-area.hpp"
Ratan Guptacb0d4e52016-12-22 19:05:57 +053017#include "fru-gen.hpp"
Vishwa4be4b7a2015-10-31 22:55:50 -050018
19// OpenBMC System Manager dbus framework
vishwa13555bd2015-11-10 12:10:38 -060020const char *sys_object_name = "/org/openbmc/managers/System";
21const char *sys_intf_name = "org.openbmc.managers.System";
Vishwa4be4b7a2015-10-31 22:55:50 -050022
Ratan Guptacb0d4e52016-12-22 19:05:57 +053023extern const FruMap frus;
24
25// Association between interface and the dbus property
26using InterfaceList = std::map<std::string,
27 std::map<std::string, std::string>>;
28
29// Association between property and its value
30using PropertiesList = std::map<std::string, std::string>;
31
32
vishwac93d6d42015-12-16 11:55:16 -060033//----------------------------------------------------------------
34// Constructor
35//----------------------------------------------------------------
36ipmi_fru::ipmi_fru(const uint8_t fruid, const ipmi_fru_area_type type,
37 sd_bus *bus_type, bool bmc_fru)
38{
39 iv_fruid = fruid;
40 iv_type = type;
41 iv_bmc_fru = bmc_fru;
42 iv_bus_type = bus_type;
43 iv_valid = false;
44 iv_data = NULL;
45 iv_present = false;
46
47 if(iv_type == IPMI_FRU_AREA_INTERNAL_USE)
48 {
49 iv_name = "INTERNAL_";
50 }
51 else if(iv_type == IPMI_FRU_AREA_CHASSIS_INFO)
52 {
53 iv_name = "CHASSIS_";
54 }
55 else if(iv_type == IPMI_FRU_AREA_BOARD_INFO)
56 {
57 iv_name = "BOARD_";
58 }
59 else if(iv_type == IPMI_FRU_AREA_PRODUCT_INFO)
60 {
61 iv_name = "PRODUCT_";
62 }
63 else if(iv_type == IPMI_FRU_AREA_MULTI_RECORD)
64 {
65 iv_name = "MULTI_";
66 }
67 else
68 {
69 iv_name = IPMI_FRU_AREA_TYPE_MAX;
70 fprintf(stderr, "ERROR: Invalid Area type :[%d]\n",iv_type);
71 }
72}
73
74//-----------------------------------------------------
75// For a FRU area type, accepts the data and updates
76// area specific data.
77//-----------------------------------------------------
78void ipmi_fru::set_data(const uint8_t *data, const size_t len)
79{
80 iv_len = len;
81 iv_data = new uint8_t[len];
82 memcpy(iv_data, data, len);
83}
84
85//-----------------------------------------------------
86// Sets the dbus parameters
87//-----------------------------------------------------
88void ipmi_fru::update_dbus_paths(const char *bus_name,
89 const char *obj_path, const char *intf_name)
90{
91 iv_bus_name = bus_name;
92 iv_obj_path = obj_path;
93 iv_intf_name = intf_name;
94}
95
96//-------------------
97// Destructor
98//-------------------
99ipmi_fru::~ipmi_fru()
100{
101 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
102 sd_bus_message *response = NULL;
103 int rc = 0;
104
105 if(iv_data != NULL)
106 {
107 delete [] iv_data;
108 iv_data = NULL;
109 }
110
111 // If we have not been successful in doing some updates and we are a BMC
112 // fru, then need to set the fault bits.
113 bool valid_dbus = !(iv_bus_name.empty()) &&
114 !(iv_obj_path.empty()) &&
115 !(iv_intf_name.empty());
116
117 // Based on bmc_fru, success in updating the FRU inventory we need to set
118 // some special bits.
119 if(iv_bmc_fru && valid_dbus)
120 {
121 // Set the Fault bit if we did not successfully process the fru
122 const char *fault_bit = iv_valid ? "False" : "True";
123
124 rc = sd_bus_call_method(iv_bus_type, // On the System Bus
125 iv_bus_name.c_str(), // Service to contact
126 iv_obj_path.c_str(), // Object path
127 iv_intf_name.c_str(), // Interface name
128 "setFault", // Method to be called
129 &bus_error, // object to return error
130 &response, // Response message on success
131 "s", // input message (string)
132 fault_bit); // First argument to setFault
133
134 if(rc <0)
135 {
136 fprintf(stderr,"Failed to set Fault bit, value:[%s] for fruid:[%d], path:[%s]\n",
137 fault_bit, iv_fruid, iv_obj_path.c_str());
138 }
139 else
140 {
141 printf("Fault bit set to :[%s] for fruid:[%d], Path:[%s]\n",
142 fault_bit, iv_fruid,iv_obj_path.c_str());
143 }
144
145 sd_bus_error_free(&bus_error);
146 sd_bus_message_unref(response);
147
148 // Set the Present bits
149 const char *present_bit = iv_present ? "True" : "False";
150
151 rc = sd_bus_call_method(iv_bus_type, // On the System Bus
152 iv_bus_name.c_str(), // Service to contact
153 iv_obj_path.c_str(), // Object path
154 iv_intf_name.c_str(), // Interface name
155 "setPresent", // Method to be called
156 &bus_error, // object to return error
157 &response, // Response message on success
158 "s", // input message (string)
159 present_bit); // First argument to setPresent
160 if(rc < 0)
161 {
162 fprintf(stderr,"Failed to set Present bit for fruid:[%d], path:[%s]\n",
163 iv_fruid, iv_obj_path.c_str());
164 }
165 else
166 {
Yi Li75c2d462016-04-11 16:57:46 +0800167 printf("Present bit set to :[%s] for fruid:[%d], Path[%s]:\n",
168 present_bit, iv_fruid, iv_obj_path.c_str());
vishwac93d6d42015-12-16 11:55:16 -0600169 }
170
171 sd_bus_error_free(&bus_error);
172 sd_bus_message_unref(response);
173 }
174}
175
176// Sets up the sd_bus structures for the given fru type
177int ipmi_fru::setup_sd_bus_paths(void)
178{
179 // Need this to get respective DBUS objects
180 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
181 sd_bus_message *response = NULL;
182 int rc = 0;
183
184 // What we need is BOARD_1, PRODUCT_1, CHASSIS_1 etc..
Patrick Williams867da972016-08-17 17:40:05 -0500185 char *inv_bus_name = NULL;
186 const char *inv_obj_path = NULL,
Patrick Williams804077d2016-07-29 15:26:18 -0500187 *inv_intf_name = NULL;
vishwac93d6d42015-12-16 11:55:16 -0600188 char fru_area_name[16] = {0};
Brad Bishopde6a3792016-07-25 17:09:12 -0400189 char *sys_bus_name = NULL;
vishwac93d6d42015-12-16 11:55:16 -0600190 sprintf(fru_area_name,"%s%d",iv_name.c_str(), iv_fruid);
191
192#ifdef __IPMI_DEBUG__
193 printf("Getting sd_bus for :[%s]\n",fru_area_name);
194#endif
195
Brad Bishopde6a3792016-07-25 17:09:12 -0400196 rc = mapper_get_service(iv_bus_type, sys_object_name, &sys_bus_name);
197 if(rc < 0)
198 {
199 fprintf(stderr, "Failed to get system manager service:[%s]\n",
200 strerror(-rc));
201 goto exit;
202 }
203
vishwac93d6d42015-12-16 11:55:16 -0600204 // We want to call a method "getObjectFromId" on System Bus that is
205 // made available over OpenBmc system services.
Yi Li75c2d462016-04-11 16:57:46 +0800206
vishwac93d6d42015-12-16 11:55:16 -0600207 rc = sd_bus_call_method(iv_bus_type, // On the System Bus
208 sys_bus_name, // Service to contact
209 sys_object_name, // Object path
210 sys_intf_name, // Interface name
211 "getObjectFromId", // Method to be called
212 &bus_error, // object to return error
213 &response, // Response message on success
214 "ss", // input message (string,string)
215 "FRU_STR", // First argument to getObjectFromId
216 fru_area_name); // Second Argument
vishwac93d6d42015-12-16 11:55:16 -0600217 if(rc < 0)
218 {
219 fprintf(stderr, "Failed to resolve fruid:[%d] to dbus: [%s]\n", iv_fruid, bus_error.message);
220 }
221 else
222 {
Brad Bishopde6a3792016-07-25 17:09:12 -0400223 // Method getObjectFromId returns 2 parameters and all are strings, namely
224 // object_path and interface name for accessing that particular
225 // FRU over Inventory SDBUS manager. 'ss' here mentions that format.
226 rc = sd_bus_message_read(response, "(ss)", &inv_obj_path, &inv_intf_name);
vishwac93d6d42015-12-16 11:55:16 -0600227 if(rc < 0)
228 {
229 fprintf(stderr, "Failed to parse response message:[%s]\n", strerror(-rc));
230 }
231 else
232 {
Brad Bishopde6a3792016-07-25 17:09:12 -0400233 rc = mapper_get_service(iv_bus_type, inv_obj_path, &inv_bus_name);
234 if(rc < 0)
235 {
236 fprintf(stderr, "Failed to get inventory item service:[%s]\n",
237 strerror(-rc));
238 goto exit;
239 }
vishwac93d6d42015-12-16 11:55:16 -0600240 // Update the paths in the area object
241 update_dbus_paths(inv_bus_name, inv_obj_path, inv_intf_name);
242 }
243 }
244
Brad Bishopde6a3792016-07-25 17:09:12 -0400245exit:
vishwac93d6d42015-12-16 11:55:16 -0600246#ifdef __IPMI_DEBUG__
247 printf("fru_area=[%s], inv_bus_name=[%s], inv_obj_path=[%s], inv_intf_name=[%s]\n",
248 fru_area_name, inv_bus_name, inv_obj_path, inv_intf_name);
249#endif
250
Brad Bishopde6a3792016-07-25 17:09:12 -0400251 free(sys_bus_name);
Patrick Williams867da972016-08-17 17:40:05 -0500252 free(inv_bus_name);
vishwac93d6d42015-12-16 11:55:16 -0600253 sd_bus_error_free(&bus_error);
254 sd_bus_message_unref(response);
255
256 return rc;
257}
258
Vishwa4be4b7a2015-10-31 22:55:50 -0500259//------------------------------------------------
vishwaf3ca3522015-12-02 10:35:13 -0600260// Takes the pointer to stream of bytes and length
vishwac93d6d42015-12-16 11:55:16 -0600261// and returns the 8 bit checksum
262// This algo is per IPMI V2.0 spec
Vishwa4be4b7a2015-10-31 22:55:50 -0500263//-------------------------------------------------
vishwac93d6d42015-12-16 11:55:16 -0600264unsigned char calculate_crc(const unsigned char *data, size_t len)
Vishwa4be4b7a2015-10-31 22:55:50 -0500265{
266 char crc = 0;
vishwac93d6d42015-12-16 11:55:16 -0600267 size_t byte = 0;
Vishwa4be4b7a2015-10-31 22:55:50 -0500268
269 for(byte = 0; byte < len; byte++)
270 {
271 crc += *data++;
272 }
vishwaf3ca3522015-12-02 10:35:13 -0600273
Vishwa4be4b7a2015-10-31 22:55:50 -0500274 return(-crc);
275}
276
277//---------------------------------------------------------------------
278// Accepts a fru area offset in commom hdr and tells which area it is.
279//---------------------------------------------------------------------
vishwac93d6d42015-12-16 11:55:16 -0600280ipmi_fru_area_type get_fru_area_type(uint8_t area_offset)
Vishwa4be4b7a2015-10-31 22:55:50 -0500281{
282 ipmi_fru_area_type type = IPMI_FRU_AREA_TYPE_MAX;
283
284 switch(area_offset)
285 {
286 case IPMI_FRU_INTERNAL_OFFSET:
287 type = IPMI_FRU_AREA_INTERNAL_USE;
288 break;
289
290 case IPMI_FRU_CHASSIS_OFFSET:
291 type = IPMI_FRU_AREA_CHASSIS_INFO;
292 break;
293
294 case IPMI_FRU_BOARD_OFFSET:
295 type = IPMI_FRU_AREA_BOARD_INFO;
296 break;
297
298 case IPMI_FRU_PRODUCT_OFFSET:
299 type = IPMI_FRU_AREA_PRODUCT_INFO;
300 break;
301
302 case IPMI_FRU_MULTI_OFFSET:
303 type = IPMI_FRU_AREA_MULTI_RECORD;
304 break;
305
306 default:
307 type = IPMI_FRU_AREA_TYPE_MAX;
308 }
309
310 return type;
311}
312
vishwac93d6d42015-12-16 11:55:16 -0600313///-----------------------------------------------
314// Validates the data for crc and mandatory fields
315///-----------------------------------------------
316int verify_fru_data(const uint8_t *data, const size_t len)
317{
318 uint8_t checksum = 0;
319 int rc = -1;
320
321 // Validate for first byte to always have a value of [1]
322 if(data[0] != IPMI_FRU_HDR_BYTE_ZERO)
323 {
324 fprintf(stderr, "Invalid entry:[%d] in byte-0\n",data[0]);
325 return rc;
326 }
327#ifdef __IPMI_DEBUG__
328 else
329 {
330 printf("SUCCESS: Validated [0x%X] in entry_1 of fru_data\n",data[0]);
331 }
332#endif
333
334 // See if the calculated CRC matches with the embedded one.
335 // CRC to be calculated on all except the last one that is CRC itself.
336 checksum = calculate_crc(data, len - 1);
337 if(checksum != data[len-1])
338 {
339#ifdef __IPMI_DEBUG__
340 fprintf(stderr, "Checksum mismatch."
341 " Calculated:[0x%X], Embedded:[0x%X]\n",
342 checksum, data[len]);
343#endif
344 return rc;
345 }
346#ifdef __IPMI_DEBUG__
347 else
348 {
349 printf("SUCCESS: Checksum matches:[0x%X]\n",checksum);
350 }
351#endif
352
353 return EXIT_SUCCESS;
354}
355
Vishwa4be4b7a2015-10-31 22:55:50 -0500356//------------------------------------------------------------------------
Ratan Guptacb0d4e52016-12-22 19:05:57 +0530357// Gets the value of the key from the fru dictionary of the given section.
358// FRU dictionary is parsed fru data for all the sections.
359//------------------------------------------------------------------------
360
361std::string getFRUValue(const std::string& section,
362 const std::string& key,
363 IPMIFruInfo& fruData)
364{
365
366 auto minIndexValue = 0;
367 auto maxIndexValue = 0;
368 std::string fruValue = "";
369 if (section == "Board")
370 {
371 minIndexValue = OPENBMC_VPD_KEY_BOARD_MFG_DATE;
372 maxIndexValue = OPENBMC_VPD_KEY_BOARD_MAX;
373 }
374 else if (section == "Product")
375 {
376 minIndexValue = OPENBMC_VPD_KEY_PRODUCT_MFR;
377 maxIndexValue = OPENBMC_VPD_KEY_PRODUCT_MAX;
378
379 }
380 else if (section == "Chassis")
381 {
382 minIndexValue = OPENBMC_VPD_KEY_CHASSIS_TYPE;
383 maxIndexValue = OPENBMC_VPD_KEY_CHASSIS_MAX;
384 }
385
386 auto first = fruData.cbegin() + minIndexValue;
387 auto last = first + (maxIndexValue - minIndexValue) + 1;
388
389 auto itr = std::find_if(first, last,
390 [&key](auto& e){ return key == e.first; });
391
392 if (itr != last)
393 {
394 fruValue = itr->second;
395 }
396 return fruValue;
397
398}
399
400// TODO: Remove once the call to inventory manager is added
401auto print = [](const InterfaceList& object, const std::string& path)
402{
403 std::cout << "\n";
404 std::cout << path << "\n";
405 std::cout << "\n";
406 for(const auto& o : object)
407 {
408 std::cout << o.first << "\n";
409 for(const auto& i : o.second)
410 {
411 std::cout << i.first << " : " << i.second << "\n";
412 }
413 std::cout << "\n";
414 }
415};
416
417//------------------------------------------------------------------------
Vishwa4be4b7a2015-10-31 22:55:50 -0500418// Takes FRU data, invokes Parser for each fru record area and updates
419// Inventory
420//------------------------------------------------------------------------
Ratan Guptacb0d4e52016-12-22 19:05:57 +0530421int ipmi_update_inventory(fru_area_vec_t& area_vec)
Vishwa4be4b7a2015-10-31 22:55:50 -0500422{
vishwac93d6d42015-12-16 11:55:16 -0600423 // Generic error reporter
Vishwa4be4b7a2015-10-31 22:55:50 -0500424 int rc = 0;
Ratan Guptacb0d4e52016-12-22 19:05:57 +0530425 uint8_t fruid = 0;
426 IPMIFruInfo fruData;
Vishwa4be4b7a2015-10-31 22:55:50 -0500427
Vishwa4be4b7a2015-10-31 22:55:50 -0500428 // For each FRU area, extract the needed data , get it parsed and update
429 // the Inventory.
Ratan Guptacb0d4e52016-12-22 19:05:57 +0530430 for (const auto& fruArea : area_vec)
Vishwa4be4b7a2015-10-31 22:55:50 -0500431 {
Ratan Guptacb0d4e52016-12-22 19:05:57 +0530432 fruid = fruArea->get_fruid();
Vishwa4be4b7a2015-10-31 22:55:50 -0500433 // Fill the container with information
Ratan Guptacb0d4e52016-12-22 19:05:57 +0530434 rc = parse_fru_area((fruArea)->get_type(), (void*)(fruArea)->get_data(),
435 (fruArea)->get_len(), fruData);
436 if (rc < 0)
Vishwa4be4b7a2015-10-31 22:55:50 -0500437 {
Ratan Guptacb0d4e52016-12-22 19:05:57 +0530438 std::cerr << "ERROR parsing FRU records\n";
439 return rc;
Vishwa4be4b7a2015-10-31 22:55:50 -0500440 }
441 } // END walking the vector of areas and updating
442
Ratan Guptacb0d4e52016-12-22 19:05:57 +0530443 // For each Fru we have the list of instances which needs to be updated.
444 // Each instance object implements certain interfaces.
445 // Each Interface is having Dbus properties.
446 // Each Dbus Property would be having metaData(eg section,VpdPropertyName).
447
448 // Here we are just printing the object,interface and the properties.
449 // which needs to be called with the new inventory manager implementation.
450 // TODO:- Call the new Inventory Manager.
451 auto iter = frus.find(fruid);
452 if (iter == frus.end())
453 {
454 std::cerr << "ERROR Unable to get the fru info for FRU=" << fruid << "\n";
455 return -1;
456 }
457 auto& instanceList = iter->second;
458
459 if (instanceList.size() <= 0)
460 {
461 std::cout << "Object List empty for this FRU=" << fruid << "\n";
462 }
463 for (auto& instance : instanceList)
464 {
465 InterfaceList interfaces;
466
467 for (auto& interfaceList : instance.second)
468 {
469 PropertiesList prop;//store all the properties
470 for (auto& properties : interfaceList.second)
471 {
472 std::string section, property, value;
473 for (auto& info : properties.second)
474 {
475 if (info.first == "IPMIFruSection")
476 {
477 section = std::move(info.second);
478 }
479 if (info.first == "IPMIFruProperty")
480 {
481 property = std::move(info.second);
482 }
483 }
484
485 if (!section.empty() && !property.empty())
486 {
487 value = getFRUValue(section, property, fruData);
488 }
489 prop.emplace(std::move(properties.first), std::move(value));
490 }
491 interfaces.emplace(std::move(interfaceList.first), std::move(prop));
492 }
493 //TODO:- remove it later with the inventory manager call.
494 print(interfaces, instance.first);
495 }
Vishwa4be4b7a2015-10-31 22:55:50 -0500496
497 return rc;
498}
499
vishwac93d6d42015-12-16 11:55:16 -0600500///----------------------------------------------------
501// Checks if a particular fru area is populated or not
502///----------------------------------------------------
503bool remove_invalid_area(const std::unique_ptr<ipmi_fru> &fru_area)
Vishwa4be4b7a2015-10-31 22:55:50 -0500504{
vishwac93d6d42015-12-16 11:55:16 -0600505 // Filter the ones that do not have dbus reference.
506 if((strlen((fru_area)->get_bus_name()) == 0) ||
507 (strlen((fru_area)->get_obj_path()) == 0) ||
508 (strlen((fru_area)->get_intf_name()) == 0))
509 {
510 return true;
511 }
512 return false;
513}
Vishwa4be4b7a2015-10-31 22:55:50 -0500514
vishwac93d6d42015-12-16 11:55:16 -0600515///----------------------------------------------------------------------------------
516// Populates various FRU areas
517// @prereq : This must be called only after validating common header.
518///----------------------------------------------------------------------------------
519int ipmi_populate_fru_areas(uint8_t *fru_data, const size_t data_len,
520 fru_area_vec_t & fru_area_vec)
521{
vishwa13555bd2015-11-10 12:10:38 -0600522 size_t area_offset = 0;
vishwac93d6d42015-12-16 11:55:16 -0600523 int rc = -1;
Vishwa4be4b7a2015-10-31 22:55:50 -0500524
vishwac93d6d42015-12-16 11:55:16 -0600525 // Now walk the common header and see if the file size has atleast the last
526 // offset mentioned by the common_hdr. If the file size is less than the
527 // offset of any if the fru areas mentioned in the common header, then we do
528 // not have a complete file.
529 for(uint8_t fru_entry = IPMI_FRU_INTERNAL_OFFSET;
530 fru_entry < (sizeof(struct common_header) -2); fru_entry++)
531 {
Yi Li75c2d462016-04-11 16:57:46 +0800532 rc = -1;
vishwac93d6d42015-12-16 11:55:16 -0600533 // Actual offset in the payload is the offset mentioned in common header
534 // multipled by 8. Common header is always the first 8 bytes.
535 area_offset = fru_data[fru_entry] * IPMI_EIGHT_BYTES;
536 if(area_offset && (data_len < (area_offset + 2)))
537 {
538 // Our file size is less than what it needs to be. +2 because we are
539 // using area len that is at 2 byte off area_offset
Patrick Williams3365ec82016-08-17 17:45:18 -0500540 fprintf(stderr, "fru file is incomplete. Size:[%zd]\n",data_len);
vishwac93d6d42015-12-16 11:55:16 -0600541 return rc;
542 }
543 else if(area_offset)
544 {
545 // Read 2 bytes to know the actual size of area.
546 uint8_t area_hdr[2] = {0};
547 memcpy(area_hdr, &((uint8_t *)fru_data)[area_offset], sizeof(area_hdr));
Vishwa4be4b7a2015-10-31 22:55:50 -0500548
vishwac93d6d42015-12-16 11:55:16 -0600549 // Size of this area will be the 2nd byte in the fru area header.
550 size_t area_len = area_hdr[1] * IPMI_EIGHT_BYTES;
551 uint8_t area_data[area_len] = {0};
Vishwa4be4b7a2015-10-31 22:55:50 -0500552
Patrick Williams3365ec82016-08-17 17:45:18 -0500553 printf("fru data size:[%zd], area offset:[%zd], area_size:[%zd]\n",
vishwac93d6d42015-12-16 11:55:16 -0600554 data_len, area_offset, area_len);
Vishwa4be4b7a2015-10-31 22:55:50 -0500555
vishwac93d6d42015-12-16 11:55:16 -0600556 // See if we really have that much buffer. We have area offset amd
557 // from there, the actual len.
558 if(data_len < (area_len + area_offset))
559 {
Patrick Williams3365ec82016-08-17 17:45:18 -0500560 fprintf(stderr, "Incomplete Fru file.. Size:[%zd]\n",data_len);
vishwac93d6d42015-12-16 11:55:16 -0600561 return rc;
562 }
563
564 // Save off the data.
565 memcpy(area_data, &((uint8_t *)fru_data)[area_offset], area_len);
566
567 // Validate the crc
568 rc = verify_fru_data(area_data, area_len);
569 if(rc < 0)
570 {
Patrick Williams3365ec82016-08-17 17:45:18 -0500571 fprintf(stderr, "Error validating fru area. offset:[%zd]\n",area_offset);
vishwac93d6d42015-12-16 11:55:16 -0600572 return rc;
573 }
574 else
575 {
Patrick Williams3365ec82016-08-17 17:45:18 -0500576 printf("Successfully verified area checksum. offset:[%zd]\n",area_offset);
vishwac93d6d42015-12-16 11:55:16 -0600577 }
578
579 // We already have a vector that is passed to us containing all
580 // of the fields populated. Update the data portion now.
581 for(auto& iter : fru_area_vec)
582 {
583 if((iter)->get_type() == get_fru_area_type(fru_entry))
584 {
585 (iter)->set_data(area_data, area_len);
586 }
587 }
588 } // If we have fru data present
589 } // Walk common_hdr
590
591 // Not all the fields will be populated in a fru data. Mostly all cases will
592 // not have more than 2 or 3.
593 fru_area_vec.erase(std::remove_if(fru_area_vec.begin(), fru_area_vec.end(),
594 remove_invalid_area), fru_area_vec.end());
595
596 return EXIT_SUCCESS;
597}
598
599///---------------------------------------------------------
600// Validates the fru data per ipmi common header constructs.
601// Returns with updated common_hdr and also file_size
602//----------------------------------------------------------
603int ipmi_validate_common_hdr(const uint8_t *fru_data, const size_t data_len)
604{
605 int rc = -1;
Vishwa4be4b7a2015-10-31 22:55:50 -0500606
607 uint8_t common_hdr[sizeof(struct common_header)] = {0};
vishwac93d6d42015-12-16 11:55:16 -0600608 if(data_len >= sizeof(common_hdr))
Vishwa4be4b7a2015-10-31 22:55:50 -0500609 {
vishwac93d6d42015-12-16 11:55:16 -0600610 memcpy(common_hdr, fru_data, sizeof(common_hdr));
Vishwa4be4b7a2015-10-31 22:55:50 -0500611 }
612 else
613 {
Patrick Williams3365ec82016-08-17 17:45:18 -0500614 fprintf(stderr, "Incomplete fru data file. Size:[%zd]\n", data_len);
vishwac93d6d42015-12-16 11:55:16 -0600615 return rc;
Vishwa4be4b7a2015-10-31 22:55:50 -0500616 }
617
vishwac93d6d42015-12-16 11:55:16 -0600618 // Verify the crc and size
619 rc = verify_fru_data(common_hdr, sizeof(common_hdr));
620 if(rc < 0)
Vishwa4be4b7a2015-10-31 22:55:50 -0500621 {
vishwac93d6d42015-12-16 11:55:16 -0600622 fprintf(stderr, "Failed to validate common header\n");
623 return rc;
Vishwa4be4b7a2015-10-31 22:55:50 -0500624 }
625
vishwac93d6d42015-12-16 11:55:16 -0600626 return EXIT_SUCCESS;
627}
Vishwa4be4b7a2015-10-31 22:55:50 -0500628
vishwac93d6d42015-12-16 11:55:16 -0600629//------------------------------------------------------------
630// Cleanup routine
631//------------------------------------------------------------
632int cleanup_error(FILE *fru_fp, fru_area_vec_t & fru_area_vec)
633{
634 if(fru_fp != NULL)
Vishwa4be4b7a2015-10-31 22:55:50 -0500635 {
vishwac93d6d42015-12-16 11:55:16 -0600636 fclose(fru_fp);
637 fru_fp = NULL;
638 }
vishwaf3ca3522015-12-02 10:35:13 -0600639
Vishwa4be4b7a2015-10-31 22:55:50 -0500640 if(!(fru_area_vec.empty()))
641 {
vishwac93d6d42015-12-16 11:55:16 -0600642 fru_area_vec.clear();
Vishwa4be4b7a2015-10-31 22:55:50 -0500643 }
vishwaf3ca3522015-12-02 10:35:13 -0600644
vishwac93d6d42015-12-16 11:55:16 -0600645 return -1;
Vishwa4be4b7a2015-10-31 22:55:50 -0500646}
647
Yi Li75c2d462016-04-11 16:57:46 +0800648
649///-----------------------------------------------------
650// Get the fru area names defined in BMC for a given @fruid.
651//----------------------------------------------------
652int get_defined_fru_area(sd_bus *bus_type, const uint8_t fruid,
653 std::vector<std::string> &defined_fru_area)
654{
655 // Need this to get respective DBUS objects
656 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
657 sd_bus_message *response = NULL;
658 int rc = 0;
Patrick Williams804077d2016-07-29 15:26:18 -0500659 const char *areas = NULL;
Brad Bishopde6a3792016-07-25 17:09:12 -0400660 char *sys_bus_name = NULL;
Yi Li75c2d462016-04-11 16:57:46 +0800661
662#ifdef __IPMI_DEBUG__
663 printf("Getting fru areas defined in Skeleton for :[%d]\n", fruid);
664#endif
665
Brad Bishopde6a3792016-07-25 17:09:12 -0400666 rc = mapper_get_service(bus_type, sys_object_name, &sys_bus_name);
667 if(rc < 0)
668 {
669 fprintf(stderr, "Failed to get system manager service:[%s]\n",
670 strerror(-rc));
671 goto exit;
672 }
673
Yi Li75c2d462016-04-11 16:57:46 +0800674 // We want to call a method "getFRUArea" on System Bus that is
675 // made available over OpenBmc system services.
676 rc = sd_bus_call_method(bus_type, // On the System Bus
677 sys_bus_name, // Service to contact
678 sys_object_name, // Object path
679 sys_intf_name, // Interface name
680 "getFRUArea", // Method to be called
681 &bus_error, // object to return error
682 &response, // Response message on success
683 "y", // input message (integer)
684 fruid); // Argument
685
686 if(rc < 0)
687 {
688 fprintf(stderr, "Failed to get fru area for fruid:[%d] to dbus: [%s]\n",
689 fruid, bus_error.message);
690 }
691 else
692 {
693 // if several fru area names are defined, the names are combined to
694 // a string seperated by ','
695 rc = sd_bus_message_read(response, "s", &areas);
696 if(rc < 0)
697 {
698 fprintf(stderr, "Failed to parse response message from getFRUArea:[%s]\n",
699 strerror(-rc));
700 }
701 else
702 {
703#ifdef __IPMI_DEBUG__
704 printf("get defined fru area: id: %d, areas: %s\n", fruid, areas);
705#endif
706 std::string area_name;
707 std::stringstream ss(areas);
708 // fru area names string is seperated by ',', parse it into tokens
709 while (std::getline(ss, area_name, ','))
710 {
711 if (!area_name.empty())
712 defined_fru_area.emplace_back(area_name);
713 }
714 }
715 }
716
Brad Bishopde6a3792016-07-25 17:09:12 -0400717exit:
718
719 free(sys_bus_name);
Yi Li75c2d462016-04-11 16:57:46 +0800720 sd_bus_error_free(&bus_error);
721 sd_bus_message_unref(response);
722
723 return rc;
724}
725
726
Vishwa4be4b7a2015-10-31 22:55:50 -0500727///-----------------------------------------------------
728// Accepts the filename and validates per IPMI FRU spec
729//----------------------------------------------------
vishwaf3ca3522015-12-02 10:35:13 -0600730int ipmi_validate_fru_area(const uint8_t fruid, const char *fru_file_name,
vishwac93d6d42015-12-16 11:55:16 -0600731 sd_bus *bus_type, const bool bmc_fru)
Vishwa4be4b7a2015-10-31 22:55:50 -0500732{
vishwac93d6d42015-12-16 11:55:16 -0600733 size_t data_len = 0;
734 size_t bytes_read = 0;
735 int rc = -1;
Vishwa4be4b7a2015-10-31 22:55:50 -0500736
vishwac93d6d42015-12-16 11:55:16 -0600737 // Vector that holds individual IPMI FRU AREAs. Although MULTI and INTERNAL
738 // are not used, keeping it here for completeness.
739 fru_area_vec_t fru_area_vec;
Yi Li75c2d462016-04-11 16:57:46 +0800740 std::vector<std::string> defined_fru_area;
741
742 // BMC defines fru areas that should be present in Skeleton
743 rc = get_defined_fru_area(bus_type, fruid, defined_fru_area);
744 if(rc < 0)
745 {
746 fprintf(stderr, "ERROR: cannot get defined fru area\n");
747 return rc;
748 }
vishwac93d6d42015-12-16 11:55:16 -0600749 for(uint8_t fru_entry = IPMI_FRU_INTERNAL_OFFSET;
750 fru_entry < (sizeof(struct common_header) -2); fru_entry++)
751 {
752 // Create an object and push onto a vector.
753 std::unique_ptr<ipmi_fru> fru_area = std::make_unique<ipmi_fru>
754 (fruid, get_fru_area_type(fru_entry), bus_type, bmc_fru);
755
756 // Physically being present
vishwa2f5a3cf2016-05-30 02:25:21 -0500757 bool present = access(fru_file_name, F_OK) == 0;
vishwac93d6d42015-12-16 11:55:16 -0600758 fru_area->set_present(present);
759
Yi Li75c2d462016-04-11 16:57:46 +0800760 // Only setup dbus path for areas defined in BMC.
761 // Otherwise Skeleton will report 'not found' error
762 std::string fru_area_name = fru_area->get_name() + std::to_string(fruid);
763 auto iter = std::find(defined_fru_area.begin(), defined_fru_area.end(),
764 fru_area_name);
765 if (iter != defined_fru_area.end())
766 {
767 fru_area->setup_sd_bus_paths();
768 }
vishwac93d6d42015-12-16 11:55:16 -0600769 fru_area_vec.emplace_back(std::move(fru_area));
770 }
771
772 FILE *fru_fp = fopen(fru_file_name,"rb");
773 if(fru_fp == NULL)
Vishwa4be4b7a2015-10-31 22:55:50 -0500774 {
775 fprintf(stderr, "ERROR: opening:[%s]\n",fru_file_name);
776 perror("Error:");
vishwac93d6d42015-12-16 11:55:16 -0600777 return cleanup_error(fru_fp, fru_area_vec);
Vishwa4be4b7a2015-10-31 22:55:50 -0500778 }
779
vishwac93d6d42015-12-16 11:55:16 -0600780 // Get the size of the file to see if it meets minimum requirement
781 if(fseek(fru_fp, 0, SEEK_END))
Vishwa4be4b7a2015-10-31 22:55:50 -0500782 {
783 perror("Error:");
vishwac93d6d42015-12-16 11:55:16 -0600784 return cleanup_error(fru_fp, fru_area_vec);
Vishwa4be4b7a2015-10-31 22:55:50 -0500785 }
786
vishwac93d6d42015-12-16 11:55:16 -0600787 // Allocate a buffer to hold entire file content
788 data_len = ftell(fru_fp);
789 uint8_t fru_data[data_len] = {0};
Vishwa4be4b7a2015-10-31 22:55:50 -0500790
vishwac93d6d42015-12-16 11:55:16 -0600791 rewind(fru_fp);
792 bytes_read = fread(fru_data, data_len, 1, fru_fp);
Vishwa4be4b7a2015-10-31 22:55:50 -0500793 if(bytes_read != 1)
794 {
Patrick Williams3365ec82016-08-17 17:45:18 -0500795 fprintf(stderr, "Failed reading fru data. Bytes_read=[%zd]\n",bytes_read);
Vishwa4be4b7a2015-10-31 22:55:50 -0500796 perror("Error:");
vishwac93d6d42015-12-16 11:55:16 -0600797 return cleanup_error(fru_fp, fru_area_vec);
Vishwa4be4b7a2015-10-31 22:55:50 -0500798 }
Vishwa4be4b7a2015-10-31 22:55:50 -0500799
vishwac93d6d42015-12-16 11:55:16 -0600800 // We are done reading.
801 fclose(fru_fp);
802 fru_fp = NULL;
803
804 rc = ipmi_validate_common_hdr(fru_data, data_len);
vishwaf3ca3522015-12-02 10:35:13 -0600805 if(rc < 0)
Vishwa4be4b7a2015-10-31 22:55:50 -0500806 {
vishwac93d6d42015-12-16 11:55:16 -0600807 return cleanup_error(fru_fp, fru_area_vec);
808 }
809
810 // Now that we validated the common header, populate various fru sections if we have them here.
811 rc = ipmi_populate_fru_areas(fru_data, data_len, fru_area_vec);
812 if(rc < 0)
813 {
814 fprintf(stderr,"Populating FRU areas failed for:[%d]\n",fruid);
815 return cleanup_error(fru_fp, fru_area_vec);
Vishwa4be4b7a2015-10-31 22:55:50 -0500816 }
817 else
818 {
vishwac93d6d42015-12-16 11:55:16 -0600819 printf("SUCCESS: Populated FRU areas for:[%s]\n",fru_file_name);
Vishwa4be4b7a2015-10-31 22:55:50 -0500820 }
821
vishwac93d6d42015-12-16 11:55:16 -0600822#ifdef __IPMI_DEBUG__
823 for(auto& iter : fru_area_vec)
Vishwa4be4b7a2015-10-31 22:55:50 -0500824 {
vishwac93d6d42015-12-16 11:55:16 -0600825 printf("FRU ID : [%d]\n",(iter)->get_fruid());
826 printf("AREA NAME : [%s]\n",(iter)->get_name());
827 printf("TYPE : [%d]\n",(iter)->get_type());
828 printf("LEN : [%d]\n",(iter)->get_len());
829 printf("BUS NAME : [%s]\n", (iter)->get_bus_name());
830 printf("OBJ PATH : [%s]\n", (iter)->get_obj_path());
831 printf("INTF NAME :[%s]\n", (iter)->get_intf_name());
Vishwa4be4b7a2015-10-31 22:55:50 -0500832 }
vishwac93d6d42015-12-16 11:55:16 -0600833#endif
834
835 // If the vector is populated with everything, then go ahead and update the
836 // inventory.
837 if(!(fru_area_vec.empty()))
838 {
839
840#ifdef __IPMI_DEBUG__
841 printf("\n SIZE of vector is : [%d] \n",fru_area_vec.size());
842#endif
843 rc = ipmi_update_inventory(fru_area_vec);
844 if(rc <0)
845 {
846 fprintf(stderr, "Error updating inventory\n");
847 }
848 }
849
850 // we are done with all that we wanted to do. This will do the job of
851 // calling any destructors too.
852 fru_area_vec.clear();
Vishwa4be4b7a2015-10-31 22:55:50 -0500853
854 return rc;
855}