blob: ee94d79858bac974b3fc5254d729eb14b0d497b1 [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"
16#include "fru-area.H"
Vishwa4be4b7a2015-10-31 22:55:50 -050017
18// OpenBMC System Manager dbus framework
vishwa13555bd2015-11-10 12:10:38 -060019const char *sys_object_name = "/org/openbmc/managers/System";
20const char *sys_intf_name = "org.openbmc.managers.System";
Vishwa4be4b7a2015-10-31 22:55:50 -050021
vishwac93d6d42015-12-16 11:55:16 -060022//----------------------------------------------------------------
23// Constructor
24//----------------------------------------------------------------
25ipmi_fru::ipmi_fru(const uint8_t fruid, const ipmi_fru_area_type type,
26 sd_bus *bus_type, bool bmc_fru)
27{
28 iv_fruid = fruid;
29 iv_type = type;
30 iv_bmc_fru = bmc_fru;
31 iv_bus_type = bus_type;
32 iv_valid = false;
33 iv_data = NULL;
34 iv_present = false;
35
36 if(iv_type == IPMI_FRU_AREA_INTERNAL_USE)
37 {
38 iv_name = "INTERNAL_";
39 }
40 else if(iv_type == IPMI_FRU_AREA_CHASSIS_INFO)
41 {
42 iv_name = "CHASSIS_";
43 }
44 else if(iv_type == IPMI_FRU_AREA_BOARD_INFO)
45 {
46 iv_name = "BOARD_";
47 }
48 else if(iv_type == IPMI_FRU_AREA_PRODUCT_INFO)
49 {
50 iv_name = "PRODUCT_";
51 }
52 else if(iv_type == IPMI_FRU_AREA_MULTI_RECORD)
53 {
54 iv_name = "MULTI_";
55 }
56 else
57 {
58 iv_name = IPMI_FRU_AREA_TYPE_MAX;
59 fprintf(stderr, "ERROR: Invalid Area type :[%d]\n",iv_type);
60 }
61}
62
63//-----------------------------------------------------
64// For a FRU area type, accepts the data and updates
65// area specific data.
66//-----------------------------------------------------
67void ipmi_fru::set_data(const uint8_t *data, const size_t len)
68{
69 iv_len = len;
70 iv_data = new uint8_t[len];
71 memcpy(iv_data, data, len);
72}
73
74//-----------------------------------------------------
75// Sets the dbus parameters
76//-----------------------------------------------------
77void ipmi_fru::update_dbus_paths(const char *bus_name,
78 const char *obj_path, const char *intf_name)
79{
80 iv_bus_name = bus_name;
81 iv_obj_path = obj_path;
82 iv_intf_name = intf_name;
83}
84
85//-------------------
86// Destructor
87//-------------------
88ipmi_fru::~ipmi_fru()
89{
90 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
91 sd_bus_message *response = NULL;
92 int rc = 0;
93
94 if(iv_data != NULL)
95 {
96 delete [] iv_data;
97 iv_data = NULL;
98 }
99
100 // If we have not been successful in doing some updates and we are a BMC
101 // fru, then need to set the fault bits.
102 bool valid_dbus = !(iv_bus_name.empty()) &&
103 !(iv_obj_path.empty()) &&
104 !(iv_intf_name.empty());
105
106 // Based on bmc_fru, success in updating the FRU inventory we need to set
107 // some special bits.
108 if(iv_bmc_fru && valid_dbus)
109 {
110 // Set the Fault bit if we did not successfully process the fru
111 const char *fault_bit = iv_valid ? "False" : "True";
112
113 rc = sd_bus_call_method(iv_bus_type, // On the System Bus
114 iv_bus_name.c_str(), // Service to contact
115 iv_obj_path.c_str(), // Object path
116 iv_intf_name.c_str(), // Interface name
117 "setFault", // Method to be called
118 &bus_error, // object to return error
119 &response, // Response message on success
120 "s", // input message (string)
121 fault_bit); // First argument to setFault
122
123 if(rc <0)
124 {
125 fprintf(stderr,"Failed to set Fault bit, value:[%s] for fruid:[%d], path:[%s]\n",
126 fault_bit, iv_fruid, iv_obj_path.c_str());
127 }
128 else
129 {
130 printf("Fault bit set to :[%s] for fruid:[%d], Path:[%s]\n",
131 fault_bit, iv_fruid,iv_obj_path.c_str());
132 }
133
134 sd_bus_error_free(&bus_error);
135 sd_bus_message_unref(response);
136
137 // Set the Present bits
138 const char *present_bit = iv_present ? "True" : "False";
139
140 rc = sd_bus_call_method(iv_bus_type, // On the System Bus
141 iv_bus_name.c_str(), // Service to contact
142 iv_obj_path.c_str(), // Object path
143 iv_intf_name.c_str(), // Interface name
144 "setPresent", // Method to be called
145 &bus_error, // object to return error
146 &response, // Response message on success
147 "s", // input message (string)
148 present_bit); // First argument to setPresent
149 if(rc < 0)
150 {
151 fprintf(stderr,"Failed to set Present bit for fruid:[%d], path:[%s]\n",
152 iv_fruid, iv_obj_path.c_str());
153 }
154 else
155 {
Yi Li75c2d462016-04-11 16:57:46 +0800156 printf("Present bit set to :[%s] for fruid:[%d], Path[%s]:\n",
157 present_bit, iv_fruid, iv_obj_path.c_str());
vishwac93d6d42015-12-16 11:55:16 -0600158 }
159
160 sd_bus_error_free(&bus_error);
161 sd_bus_message_unref(response);
162 }
163}
164
165// Sets up the sd_bus structures for the given fru type
166int ipmi_fru::setup_sd_bus_paths(void)
167{
168 // Need this to get respective DBUS objects
169 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
170 sd_bus_message *response = NULL;
171 int rc = 0;
172
173 // What we need is BOARD_1, PRODUCT_1, CHASSIS_1 etc..
Brad Bishopde6a3792016-07-25 17:09:12 -0400174 char *inv_bus_name = NULL, *inv_obj_path, *inv_intf_name;
vishwac93d6d42015-12-16 11:55:16 -0600175 char fru_area_name[16] = {0};
Brad Bishopde6a3792016-07-25 17:09:12 -0400176 char *sys_bus_name = NULL;
vishwac93d6d42015-12-16 11:55:16 -0600177 sprintf(fru_area_name,"%s%d",iv_name.c_str(), iv_fruid);
178
179#ifdef __IPMI_DEBUG__
180 printf("Getting sd_bus for :[%s]\n",fru_area_name);
181#endif
182
Brad Bishopde6a3792016-07-25 17:09:12 -0400183 rc = mapper_get_service(iv_bus_type, sys_object_name, &sys_bus_name);
184 if(rc < 0)
185 {
186 fprintf(stderr, "Failed to get system manager service:[%s]\n",
187 strerror(-rc));
188 goto exit;
189 }
190
vishwac93d6d42015-12-16 11:55:16 -0600191 // We want to call a method "getObjectFromId" on System Bus that is
192 // made available over OpenBmc system services.
Yi Li75c2d462016-04-11 16:57:46 +0800193
vishwac93d6d42015-12-16 11:55:16 -0600194 rc = sd_bus_call_method(iv_bus_type, // On the System Bus
195 sys_bus_name, // Service to contact
196 sys_object_name, // Object path
197 sys_intf_name, // Interface name
198 "getObjectFromId", // Method to be called
199 &bus_error, // object to return error
200 &response, // Response message on success
201 "ss", // input message (string,string)
202 "FRU_STR", // First argument to getObjectFromId
203 fru_area_name); // Second Argument
vishwac93d6d42015-12-16 11:55:16 -0600204 if(rc < 0)
205 {
206 fprintf(stderr, "Failed to resolve fruid:[%d] to dbus: [%s]\n", iv_fruid, bus_error.message);
207 }
208 else
209 {
Brad Bishopde6a3792016-07-25 17:09:12 -0400210 // Method getObjectFromId returns 2 parameters and all are strings, namely
211 // object_path and interface name for accessing that particular
212 // FRU over Inventory SDBUS manager. 'ss' here mentions that format.
213 rc = sd_bus_message_read(response, "(ss)", &inv_obj_path, &inv_intf_name);
vishwac93d6d42015-12-16 11:55:16 -0600214 if(rc < 0)
215 {
216 fprintf(stderr, "Failed to parse response message:[%s]\n", strerror(-rc));
217 }
218 else
219 {
Brad Bishopde6a3792016-07-25 17:09:12 -0400220 rc = mapper_get_service(iv_bus_type, inv_obj_path, &inv_bus_name);
221 if(rc < 0)
222 {
223 fprintf(stderr, "Failed to get inventory item service:[%s]\n",
224 strerror(-rc));
225 goto exit;
226 }
vishwac93d6d42015-12-16 11:55:16 -0600227 // Update the paths in the area object
228 update_dbus_paths(inv_bus_name, inv_obj_path, inv_intf_name);
229 }
230 }
231
Brad Bishopde6a3792016-07-25 17:09:12 -0400232exit:
vishwac93d6d42015-12-16 11:55:16 -0600233#ifdef __IPMI_DEBUG__
234 printf("fru_area=[%s], inv_bus_name=[%s], inv_obj_path=[%s], inv_intf_name=[%s]\n",
235 fru_area_name, inv_bus_name, inv_obj_path, inv_intf_name);
236#endif
237
Brad Bishopde6a3792016-07-25 17:09:12 -0400238 free(sys_bus_name);
vishwac93d6d42015-12-16 11:55:16 -0600239 sd_bus_error_free(&bus_error);
240 sd_bus_message_unref(response);
241
242 return rc;
243}
244
Vishwa4be4b7a2015-10-31 22:55:50 -0500245//------------------------------------------------
vishwaf3ca3522015-12-02 10:35:13 -0600246// Takes the pointer to stream of bytes and length
vishwac93d6d42015-12-16 11:55:16 -0600247// and returns the 8 bit checksum
248// This algo is per IPMI V2.0 spec
Vishwa4be4b7a2015-10-31 22:55:50 -0500249//-------------------------------------------------
vishwac93d6d42015-12-16 11:55:16 -0600250unsigned char calculate_crc(const unsigned char *data, size_t len)
Vishwa4be4b7a2015-10-31 22:55:50 -0500251{
252 char crc = 0;
vishwac93d6d42015-12-16 11:55:16 -0600253 size_t byte = 0;
Vishwa4be4b7a2015-10-31 22:55:50 -0500254
255 for(byte = 0; byte < len; byte++)
256 {
257 crc += *data++;
258 }
vishwaf3ca3522015-12-02 10:35:13 -0600259
Vishwa4be4b7a2015-10-31 22:55:50 -0500260 return(-crc);
261}
262
263//---------------------------------------------------------------------
264// Accepts a fru area offset in commom hdr and tells which area it is.
265//---------------------------------------------------------------------
vishwac93d6d42015-12-16 11:55:16 -0600266ipmi_fru_area_type get_fru_area_type(uint8_t area_offset)
Vishwa4be4b7a2015-10-31 22:55:50 -0500267{
268 ipmi_fru_area_type type = IPMI_FRU_AREA_TYPE_MAX;
269
270 switch(area_offset)
271 {
272 case IPMI_FRU_INTERNAL_OFFSET:
273 type = IPMI_FRU_AREA_INTERNAL_USE;
274 break;
275
276 case IPMI_FRU_CHASSIS_OFFSET:
277 type = IPMI_FRU_AREA_CHASSIS_INFO;
278 break;
279
280 case IPMI_FRU_BOARD_OFFSET:
281 type = IPMI_FRU_AREA_BOARD_INFO;
282 break;
283
284 case IPMI_FRU_PRODUCT_OFFSET:
285 type = IPMI_FRU_AREA_PRODUCT_INFO;
286 break;
287
288 case IPMI_FRU_MULTI_OFFSET:
289 type = IPMI_FRU_AREA_MULTI_RECORD;
290 break;
291
292 default:
293 type = IPMI_FRU_AREA_TYPE_MAX;
294 }
295
296 return type;
297}
298
vishwac93d6d42015-12-16 11:55:16 -0600299///-----------------------------------------------
300// Validates the data for crc and mandatory fields
301///-----------------------------------------------
302int verify_fru_data(const uint8_t *data, const size_t len)
303{
304 uint8_t checksum = 0;
305 int rc = -1;
306
307 // Validate for first byte to always have a value of [1]
308 if(data[0] != IPMI_FRU_HDR_BYTE_ZERO)
309 {
310 fprintf(stderr, "Invalid entry:[%d] in byte-0\n",data[0]);
311 return rc;
312 }
313#ifdef __IPMI_DEBUG__
314 else
315 {
316 printf("SUCCESS: Validated [0x%X] in entry_1 of fru_data\n",data[0]);
317 }
318#endif
319
320 // See if the calculated CRC matches with the embedded one.
321 // CRC to be calculated on all except the last one that is CRC itself.
322 checksum = calculate_crc(data, len - 1);
323 if(checksum != data[len-1])
324 {
325#ifdef __IPMI_DEBUG__
326 fprintf(stderr, "Checksum mismatch."
327 " Calculated:[0x%X], Embedded:[0x%X]\n",
328 checksum, data[len]);
329#endif
330 return rc;
331 }
332#ifdef __IPMI_DEBUG__
333 else
334 {
335 printf("SUCCESS: Checksum matches:[0x%X]\n",checksum);
336 }
337#endif
338
339 return EXIT_SUCCESS;
340}
341
Vishwa4be4b7a2015-10-31 22:55:50 -0500342//------------------------------------------------------------------------
343// Takes FRU data, invokes Parser for each fru record area and updates
344// Inventory
345//------------------------------------------------------------------------
vishwac93d6d42015-12-16 11:55:16 -0600346int ipmi_update_inventory(fru_area_vec_t & area_vec)
Vishwa4be4b7a2015-10-31 22:55:50 -0500347{
vishwac93d6d42015-12-16 11:55:16 -0600348 // Generic error reporter
Vishwa4be4b7a2015-10-31 22:55:50 -0500349 int rc = 0;
vishwaf3ca3522015-12-02 10:35:13 -0600350
Vishwa4be4b7a2015-10-31 22:55:50 -0500351 // Dictionary object to hold Name:Value pair
352 sd_bus_message *fru_dict = NULL;
353
354 // SD Bus error report mechanism.
355 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
356
vishwac93d6d42015-12-16 11:55:16 -0600357 // Response from sd bus calls
Vishwa4be4b7a2015-10-31 22:55:50 -0500358 sd_bus_message *response = NULL;
359
Vishwa4be4b7a2015-10-31 22:55:50 -0500360 // For each FRU area, extract the needed data , get it parsed and update
361 // the Inventory.
362 for(auto& iter : area_vec)
363 {
vishwac93d6d42015-12-16 11:55:16 -0600364 // Start fresh on each.
Vishwa4be4b7a2015-10-31 22:55:50 -0500365 sd_bus_error_free(&bus_error);
366 sd_bus_message_unref(response);
367 sd_bus_message_unref(fru_dict);
vishwaf3ca3522015-12-02 10:35:13 -0600368
Vishwa4be4b7a2015-10-31 22:55:50 -0500369 // Constructor to allow further initializations and customization.
vishwac93d6d42015-12-16 11:55:16 -0600370 rc = sd_bus_message_new_method_call((iter)->get_bus_type(),
Vishwa4be4b7a2015-10-31 22:55:50 -0500371 &fru_dict,
vishwac93d6d42015-12-16 11:55:16 -0600372 (iter)->get_bus_name(),
373 (iter)->get_obj_path(),
374 (iter)->get_intf_name(),
Vishwa4be4b7a2015-10-31 22:55:50 -0500375 "update");
376 if(rc < 0)
377 {
vishwac93d6d42015-12-16 11:55:16 -0600378 fprintf(stderr,"ERROR: creating a update method call for bus_name:[%s]\n",
379 (iter)->get_bus_name());
Vishwa4be4b7a2015-10-31 22:55:50 -0500380 break;
381 }
382
383 // A Dictionary ({}) having (string, variant)
384 rc = sd_bus_message_open_container(fru_dict, 'a', "{sv}");
385 if(rc < 0)
386 {
387 fprintf(stderr,"ERROR:[%d] creating a dict container:\n",errno);
388 break;
389 }
390
391 // Fill the container with information
vishwac93d6d42015-12-16 11:55:16 -0600392 rc = parse_fru_area((iter)->get_type(), (void *)(iter)->get_data(), (iter)->get_len(), fru_dict);
Vishwa4be4b7a2015-10-31 22:55:50 -0500393 if(rc < 0)
394 {
395 fprintf(stderr,"ERROR parsing FRU records\n");
396 break;
397 }
398
399 sd_bus_message_close_container(fru_dict);
400
401 // Now, Make the actual call to update the FRU inventory database with the
402 // dictionary given by FRU Parser. There is no response message expected for
403 // this.
vishwac93d6d42015-12-16 11:55:16 -0600404 rc = sd_bus_call((iter)->get_bus_type(), // On the System Bus
405 fru_dict, // With the Name:value dictionary array
406 0, //
407 &bus_error, // Object to return error.
408 &response); // Response message if any.
Vishwa4be4b7a2015-10-31 22:55:50 -0500409
410 if(rc < 0)
411 {
412 fprintf(stderr, "ERROR:[%s] updating FRU inventory for ID:[0x%X]\n",
vishwac93d6d42015-12-16 11:55:16 -0600413 bus_error.message, (iter)->get_fruid());
414 break;
Vishwa4be4b7a2015-10-31 22:55:50 -0500415 }
vishwac93d6d42015-12-16 11:55:16 -0600416 else if((iter)->is_bmc_fru())
vishwaf3ca3522015-12-02 10:35:13 -0600417 {
vishwac93d6d42015-12-16 11:55:16 -0600418 // For FRUs that are accessible by HostBoot, host boot does all of
419 // these.
420 printf("SUCCESS: Updated:[%s_%d] successfully. Setting Valid bit\n",
421 (iter)->get_name(), (iter)->get_fruid());
vishwaf3ca3522015-12-02 10:35:13 -0600422
vishwac93d6d42015-12-16 11:55:16 -0600423 (iter)->set_valid(true);
vishwaf3ca3522015-12-02 10:35:13 -0600424 }
Vishwa4be4b7a2015-10-31 22:55:50 -0500425 else
426 {
vishwac93d6d42015-12-16 11:55:16 -0600427 printf("SUCCESS: Updated:[%s_%d] successfully\n",
428 (iter)->get_name(), (iter)->get_fruid());
Vishwa4be4b7a2015-10-31 22:55:50 -0500429 }
430 } // END walking the vector of areas and updating
431
432 sd_bus_error_free(&bus_error);
433 sd_bus_message_unref(response);
434 sd_bus_message_unref(fru_dict);
Vishwa4be4b7a2015-10-31 22:55:50 -0500435
436 return rc;
437}
438
vishwac93d6d42015-12-16 11:55:16 -0600439///----------------------------------------------------
440// Checks if a particular fru area is populated or not
441///----------------------------------------------------
442bool remove_invalid_area(const std::unique_ptr<ipmi_fru> &fru_area)
Vishwa4be4b7a2015-10-31 22:55:50 -0500443{
vishwac93d6d42015-12-16 11:55:16 -0600444 // Filter the ones that do not have dbus reference.
445 if((strlen((fru_area)->get_bus_name()) == 0) ||
446 (strlen((fru_area)->get_obj_path()) == 0) ||
447 (strlen((fru_area)->get_intf_name()) == 0))
448 {
449 return true;
450 }
451 return false;
452}
Vishwa4be4b7a2015-10-31 22:55:50 -0500453
vishwac93d6d42015-12-16 11:55:16 -0600454///----------------------------------------------------------------------------------
455// Populates various FRU areas
456// @prereq : This must be called only after validating common header.
457///----------------------------------------------------------------------------------
458int ipmi_populate_fru_areas(uint8_t *fru_data, const size_t data_len,
459 fru_area_vec_t & fru_area_vec)
460{
vishwa13555bd2015-11-10 12:10:38 -0600461 size_t area_offset = 0;
vishwac93d6d42015-12-16 11:55:16 -0600462 int rc = -1;
Vishwa4be4b7a2015-10-31 22:55:50 -0500463
vishwac93d6d42015-12-16 11:55:16 -0600464 // Now walk the common header and see if the file size has atleast the last
465 // offset mentioned by the common_hdr. If the file size is less than the
466 // offset of any if the fru areas mentioned in the common header, then we do
467 // not have a complete file.
468 for(uint8_t fru_entry = IPMI_FRU_INTERNAL_OFFSET;
469 fru_entry < (sizeof(struct common_header) -2); fru_entry++)
470 {
Yi Li75c2d462016-04-11 16:57:46 +0800471 rc = -1;
vishwac93d6d42015-12-16 11:55:16 -0600472 // Actual offset in the payload is the offset mentioned in common header
473 // multipled by 8. Common header is always the first 8 bytes.
474 area_offset = fru_data[fru_entry] * IPMI_EIGHT_BYTES;
475 if(area_offset && (data_len < (area_offset + 2)))
476 {
477 // Our file size is less than what it needs to be. +2 because we are
478 // using area len that is at 2 byte off area_offset
479 fprintf(stderr, "fru file is incomplete. Size:[%d]\n",data_len);
480 return rc;
481 }
482 else if(area_offset)
483 {
484 // Read 2 bytes to know the actual size of area.
485 uint8_t area_hdr[2] = {0};
486 memcpy(area_hdr, &((uint8_t *)fru_data)[area_offset], sizeof(area_hdr));
Vishwa4be4b7a2015-10-31 22:55:50 -0500487
vishwac93d6d42015-12-16 11:55:16 -0600488 // Size of this area will be the 2nd byte in the fru area header.
489 size_t area_len = area_hdr[1] * IPMI_EIGHT_BYTES;
490 uint8_t area_data[area_len] = {0};
Vishwa4be4b7a2015-10-31 22:55:50 -0500491
vishwac93d6d42015-12-16 11:55:16 -0600492 printf("fru data size:[%d], area offset:[%d], area_size:[%d]\n",
493 data_len, area_offset, area_len);
Vishwa4be4b7a2015-10-31 22:55:50 -0500494
vishwac93d6d42015-12-16 11:55:16 -0600495 // See if we really have that much buffer. We have area offset amd
496 // from there, the actual len.
497 if(data_len < (area_len + area_offset))
498 {
499 fprintf(stderr, "Incomplete Fru file.. Size:[%d]\n",data_len);
500 return rc;
501 }
502
503 // Save off the data.
504 memcpy(area_data, &((uint8_t *)fru_data)[area_offset], area_len);
505
506 // Validate the crc
507 rc = verify_fru_data(area_data, area_len);
508 if(rc < 0)
509 {
510 fprintf(stderr, "Error validating fru area. offset:[%d]\n",area_offset);
511 return rc;
512 }
513 else
514 {
515 printf("Successfully verified area checksum. offset:[%d]\n",area_offset);
516 }
517
518 // We already have a vector that is passed to us containing all
519 // of the fields populated. Update the data portion now.
520 for(auto& iter : fru_area_vec)
521 {
522 if((iter)->get_type() == get_fru_area_type(fru_entry))
523 {
524 (iter)->set_data(area_data, area_len);
525 }
526 }
527 } // If we have fru data present
528 } // Walk common_hdr
529
530 // Not all the fields will be populated in a fru data. Mostly all cases will
531 // not have more than 2 or 3.
532 fru_area_vec.erase(std::remove_if(fru_area_vec.begin(), fru_area_vec.end(),
533 remove_invalid_area), fru_area_vec.end());
534
535 return EXIT_SUCCESS;
536}
537
538///---------------------------------------------------------
539// Validates the fru data per ipmi common header constructs.
540// Returns with updated common_hdr and also file_size
541//----------------------------------------------------------
542int ipmi_validate_common_hdr(const uint8_t *fru_data, const size_t data_len)
543{
544 int rc = -1;
Vishwa4be4b7a2015-10-31 22:55:50 -0500545
546 uint8_t common_hdr[sizeof(struct common_header)] = {0};
vishwac93d6d42015-12-16 11:55:16 -0600547 if(data_len >= sizeof(common_hdr))
Vishwa4be4b7a2015-10-31 22:55:50 -0500548 {
vishwac93d6d42015-12-16 11:55:16 -0600549 memcpy(common_hdr, fru_data, sizeof(common_hdr));
Vishwa4be4b7a2015-10-31 22:55:50 -0500550 }
551 else
552 {
vishwac93d6d42015-12-16 11:55:16 -0600553 fprintf(stderr, "Incomplete fru data file. Size:[%d]\n", data_len);
554 return rc;
Vishwa4be4b7a2015-10-31 22:55:50 -0500555 }
556
vishwac93d6d42015-12-16 11:55:16 -0600557 // Verify the crc and size
558 rc = verify_fru_data(common_hdr, sizeof(common_hdr));
559 if(rc < 0)
Vishwa4be4b7a2015-10-31 22:55:50 -0500560 {
vishwac93d6d42015-12-16 11:55:16 -0600561 fprintf(stderr, "Failed to validate common header\n");
562 return rc;
Vishwa4be4b7a2015-10-31 22:55:50 -0500563 }
564
vishwac93d6d42015-12-16 11:55:16 -0600565 return EXIT_SUCCESS;
566}
Vishwa4be4b7a2015-10-31 22:55:50 -0500567
vishwac93d6d42015-12-16 11:55:16 -0600568//------------------------------------------------------------
569// Cleanup routine
570//------------------------------------------------------------
571int cleanup_error(FILE *fru_fp, fru_area_vec_t & fru_area_vec)
572{
573 if(fru_fp != NULL)
Vishwa4be4b7a2015-10-31 22:55:50 -0500574 {
vishwac93d6d42015-12-16 11:55:16 -0600575 fclose(fru_fp);
576 fru_fp = NULL;
577 }
vishwaf3ca3522015-12-02 10:35:13 -0600578
Vishwa4be4b7a2015-10-31 22:55:50 -0500579 if(!(fru_area_vec.empty()))
580 {
vishwac93d6d42015-12-16 11:55:16 -0600581 fru_area_vec.clear();
Vishwa4be4b7a2015-10-31 22:55:50 -0500582 }
vishwaf3ca3522015-12-02 10:35:13 -0600583
vishwac93d6d42015-12-16 11:55:16 -0600584 return -1;
Vishwa4be4b7a2015-10-31 22:55:50 -0500585}
586
Yi Li75c2d462016-04-11 16:57:46 +0800587
588///-----------------------------------------------------
589// Get the fru area names defined in BMC for a given @fruid.
590//----------------------------------------------------
591int get_defined_fru_area(sd_bus *bus_type, const uint8_t fruid,
592 std::vector<std::string> &defined_fru_area)
593{
594 // Need this to get respective DBUS objects
595 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
596 sd_bus_message *response = NULL;
597 int rc = 0;
598 char *areas = NULL;
Brad Bishopde6a3792016-07-25 17:09:12 -0400599 char *sys_bus_name = NULL;
Yi Li75c2d462016-04-11 16:57:46 +0800600
601#ifdef __IPMI_DEBUG__
602 printf("Getting fru areas defined in Skeleton for :[%d]\n", fruid);
603#endif
604
Brad Bishopde6a3792016-07-25 17:09:12 -0400605 rc = mapper_get_service(bus_type, sys_object_name, &sys_bus_name);
606 if(rc < 0)
607 {
608 fprintf(stderr, "Failed to get system manager service:[%s]\n",
609 strerror(-rc));
610 goto exit;
611 }
612
Yi Li75c2d462016-04-11 16:57:46 +0800613 // We want to call a method "getFRUArea" on System Bus that is
614 // made available over OpenBmc system services.
615 rc = sd_bus_call_method(bus_type, // On the System Bus
616 sys_bus_name, // Service to contact
617 sys_object_name, // Object path
618 sys_intf_name, // Interface name
619 "getFRUArea", // Method to be called
620 &bus_error, // object to return error
621 &response, // Response message on success
622 "y", // input message (integer)
623 fruid); // Argument
624
625 if(rc < 0)
626 {
627 fprintf(stderr, "Failed to get fru area for fruid:[%d] to dbus: [%s]\n",
628 fruid, bus_error.message);
629 }
630 else
631 {
632 // if several fru area names are defined, the names are combined to
633 // a string seperated by ','
634 rc = sd_bus_message_read(response, "s", &areas);
635 if(rc < 0)
636 {
637 fprintf(stderr, "Failed to parse response message from getFRUArea:[%s]\n",
638 strerror(-rc));
639 }
640 else
641 {
642#ifdef __IPMI_DEBUG__
643 printf("get defined fru area: id: %d, areas: %s\n", fruid, areas);
644#endif
645 std::string area_name;
646 std::stringstream ss(areas);
647 // fru area names string is seperated by ',', parse it into tokens
648 while (std::getline(ss, area_name, ','))
649 {
650 if (!area_name.empty())
651 defined_fru_area.emplace_back(area_name);
652 }
653 }
654 }
655
Brad Bishopde6a3792016-07-25 17:09:12 -0400656exit:
657
658 free(sys_bus_name);
Yi Li75c2d462016-04-11 16:57:46 +0800659 sd_bus_error_free(&bus_error);
660 sd_bus_message_unref(response);
661
662 return rc;
663}
664
665
Vishwa4be4b7a2015-10-31 22:55:50 -0500666///-----------------------------------------------------
667// Accepts the filename and validates per IPMI FRU spec
668//----------------------------------------------------
vishwaf3ca3522015-12-02 10:35:13 -0600669int ipmi_validate_fru_area(const uint8_t fruid, const char *fru_file_name,
vishwac93d6d42015-12-16 11:55:16 -0600670 sd_bus *bus_type, const bool bmc_fru)
Vishwa4be4b7a2015-10-31 22:55:50 -0500671{
vishwac93d6d42015-12-16 11:55:16 -0600672 size_t data_len = 0;
673 size_t bytes_read = 0;
674 int rc = -1;
Vishwa4be4b7a2015-10-31 22:55:50 -0500675
vishwac93d6d42015-12-16 11:55:16 -0600676 // Vector that holds individual IPMI FRU AREAs. Although MULTI and INTERNAL
677 // are not used, keeping it here for completeness.
678 fru_area_vec_t fru_area_vec;
Yi Li75c2d462016-04-11 16:57:46 +0800679 std::vector<std::string> defined_fru_area;
680
681 // BMC defines fru areas that should be present in Skeleton
682 rc = get_defined_fru_area(bus_type, fruid, defined_fru_area);
683 if(rc < 0)
684 {
685 fprintf(stderr, "ERROR: cannot get defined fru area\n");
686 return rc;
687 }
vishwac93d6d42015-12-16 11:55:16 -0600688 for(uint8_t fru_entry = IPMI_FRU_INTERNAL_OFFSET;
689 fru_entry < (sizeof(struct common_header) -2); fru_entry++)
690 {
691 // Create an object and push onto a vector.
692 std::unique_ptr<ipmi_fru> fru_area = std::make_unique<ipmi_fru>
693 (fruid, get_fru_area_type(fru_entry), bus_type, bmc_fru);
694
695 // Physically being present
vishwa2f5a3cf2016-05-30 02:25:21 -0500696 bool present = access(fru_file_name, F_OK) == 0;
vishwac93d6d42015-12-16 11:55:16 -0600697 fru_area->set_present(present);
698
Yi Li75c2d462016-04-11 16:57:46 +0800699 // Only setup dbus path for areas defined in BMC.
700 // Otherwise Skeleton will report 'not found' error
701 std::string fru_area_name = fru_area->get_name() + std::to_string(fruid);
702 auto iter = std::find(defined_fru_area.begin(), defined_fru_area.end(),
703 fru_area_name);
704 if (iter != defined_fru_area.end())
705 {
706 fru_area->setup_sd_bus_paths();
707 }
vishwac93d6d42015-12-16 11:55:16 -0600708 fru_area_vec.emplace_back(std::move(fru_area));
709 }
710
711 FILE *fru_fp = fopen(fru_file_name,"rb");
712 if(fru_fp == NULL)
Vishwa4be4b7a2015-10-31 22:55:50 -0500713 {
714 fprintf(stderr, "ERROR: opening:[%s]\n",fru_file_name);
715 perror("Error:");
vishwac93d6d42015-12-16 11:55:16 -0600716 return cleanup_error(fru_fp, fru_area_vec);
Vishwa4be4b7a2015-10-31 22:55:50 -0500717 }
718
vishwac93d6d42015-12-16 11:55:16 -0600719 // Get the size of the file to see if it meets minimum requirement
720 if(fseek(fru_fp, 0, SEEK_END))
Vishwa4be4b7a2015-10-31 22:55:50 -0500721 {
722 perror("Error:");
vishwac93d6d42015-12-16 11:55:16 -0600723 return cleanup_error(fru_fp, fru_area_vec);
Vishwa4be4b7a2015-10-31 22:55:50 -0500724 }
725
vishwac93d6d42015-12-16 11:55:16 -0600726 // Allocate a buffer to hold entire file content
727 data_len = ftell(fru_fp);
728 uint8_t fru_data[data_len] = {0};
Vishwa4be4b7a2015-10-31 22:55:50 -0500729
vishwac93d6d42015-12-16 11:55:16 -0600730 rewind(fru_fp);
731 bytes_read = fread(fru_data, data_len, 1, fru_fp);
Vishwa4be4b7a2015-10-31 22:55:50 -0500732 if(bytes_read != 1)
733 {
vishwac93d6d42015-12-16 11:55:16 -0600734 fprintf(stderr, "Failed reading fru data. Bytes_read=[%d]\n",bytes_read);
Vishwa4be4b7a2015-10-31 22:55:50 -0500735 perror("Error:");
vishwac93d6d42015-12-16 11:55:16 -0600736 return cleanup_error(fru_fp, fru_area_vec);
Vishwa4be4b7a2015-10-31 22:55:50 -0500737 }
Vishwa4be4b7a2015-10-31 22:55:50 -0500738
vishwac93d6d42015-12-16 11:55:16 -0600739 // We are done reading.
740 fclose(fru_fp);
741 fru_fp = NULL;
742
743 rc = ipmi_validate_common_hdr(fru_data, data_len);
vishwaf3ca3522015-12-02 10:35:13 -0600744 if(rc < 0)
Vishwa4be4b7a2015-10-31 22:55:50 -0500745 {
vishwac93d6d42015-12-16 11:55:16 -0600746 return cleanup_error(fru_fp, fru_area_vec);
747 }
748
749 // Now that we validated the common header, populate various fru sections if we have them here.
750 rc = ipmi_populate_fru_areas(fru_data, data_len, fru_area_vec);
751 if(rc < 0)
752 {
753 fprintf(stderr,"Populating FRU areas failed for:[%d]\n",fruid);
754 return cleanup_error(fru_fp, fru_area_vec);
Vishwa4be4b7a2015-10-31 22:55:50 -0500755 }
756 else
757 {
vishwac93d6d42015-12-16 11:55:16 -0600758 printf("SUCCESS: Populated FRU areas for:[%s]\n",fru_file_name);
Vishwa4be4b7a2015-10-31 22:55:50 -0500759 }
760
vishwac93d6d42015-12-16 11:55:16 -0600761#ifdef __IPMI_DEBUG__
762 for(auto& iter : fru_area_vec)
Vishwa4be4b7a2015-10-31 22:55:50 -0500763 {
vishwac93d6d42015-12-16 11:55:16 -0600764 printf("FRU ID : [%d]\n",(iter)->get_fruid());
765 printf("AREA NAME : [%s]\n",(iter)->get_name());
766 printf("TYPE : [%d]\n",(iter)->get_type());
767 printf("LEN : [%d]\n",(iter)->get_len());
768 printf("BUS NAME : [%s]\n", (iter)->get_bus_name());
769 printf("OBJ PATH : [%s]\n", (iter)->get_obj_path());
770 printf("INTF NAME :[%s]\n", (iter)->get_intf_name());
Vishwa4be4b7a2015-10-31 22:55:50 -0500771 }
vishwac93d6d42015-12-16 11:55:16 -0600772#endif
773
774 // If the vector is populated with everything, then go ahead and update the
775 // inventory.
776 if(!(fru_area_vec.empty()))
777 {
778
779#ifdef __IPMI_DEBUG__
780 printf("\n SIZE of vector is : [%d] \n",fru_area_vec.size());
781#endif
782 rc = ipmi_update_inventory(fru_area_vec);
783 if(rc <0)
784 {
785 fprintf(stderr, "Error updating inventory\n");
786 }
787 }
788
789 // we are done with all that we wanted to do. This will do the job of
790 // calling any destructors too.
791 fru_area_vec.clear();
Vishwa4be4b7a2015-10-31 22:55:50 -0500792
793 return rc;
794}