blob: 9b52da2f1e6500c19ff5a9b9c4375a1372399635 [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>
6#include "frup.h"
7#include "writefrudata.H"
8#include <systemd/sd-bus.h>
Chris Austenb45c4cb2015-11-01 06:34:56 -06009#include <unistd.h>
vishwa13555bd2015-11-10 12:10:38 -060010#include <host-ipmid/ipmid-api.h>
Vishwa4be4b7a2015-10-31 22:55:50 -050011
12// Needed to be passed into fru parser alorithm
13typedef std::vector<fru_area_t> fru_area_vec_t;
14
15// OpenBMC System Manager dbus framework
vishwa13555bd2015-11-10 12:10:38 -060016const char *sys_bus_name = "org.openbmc.managers.System";
17const char *sys_object_name = "/org/openbmc/managers/System";
18const char *sys_intf_name = "org.openbmc.managers.System";
Vishwa4be4b7a2015-10-31 22:55:50 -050019
20//------------------------------------------------
vishwaf3ca3522015-12-02 10:35:13 -060021// Takes the pointer to stream of bytes and length
Vishwa4be4b7a2015-10-31 22:55:50 -050022// returns the 8 bit checksum per IPMI spec.
23//-------------------------------------------------
24unsigned char calculate_crc(unsigned char *data, int len)
25{
26 char crc = 0;
27 int byte = 0;
28
29 for(byte = 0; byte < len; byte++)
30 {
31 crc += *data++;
32 }
vishwaf3ca3522015-12-02 10:35:13 -060033
Vishwa4be4b7a2015-10-31 22:55:50 -050034 return(-crc);
35}
36
37//---------------------------------------------------------------------
38// Accepts a fru area offset in commom hdr and tells which area it is.
39//---------------------------------------------------------------------
40uint8_t get_fru_area_type(uint8_t area_offset)
41{
42 ipmi_fru_area_type type = IPMI_FRU_AREA_TYPE_MAX;
43
44 switch(area_offset)
45 {
46 case IPMI_FRU_INTERNAL_OFFSET:
47 type = IPMI_FRU_AREA_INTERNAL_USE;
48 break;
49
50 case IPMI_FRU_CHASSIS_OFFSET:
51 type = IPMI_FRU_AREA_CHASSIS_INFO;
52 break;
53
54 case IPMI_FRU_BOARD_OFFSET:
55 type = IPMI_FRU_AREA_BOARD_INFO;
56 break;
57
58 case IPMI_FRU_PRODUCT_OFFSET:
59 type = IPMI_FRU_AREA_PRODUCT_INFO;
60 break;
61
62 case IPMI_FRU_MULTI_OFFSET:
63 type = IPMI_FRU_AREA_MULTI_RECORD;
64 break;
65
66 default:
67 type = IPMI_FRU_AREA_TYPE_MAX;
68 }
69
70 return type;
71}
72
73//------------------------------------------------------------------------
74// Takes FRU data, invokes Parser for each fru record area and updates
75// Inventory
76//------------------------------------------------------------------------
vishwaf3ca3522015-12-02 10:35:13 -060077int ipmi_update_inventory(const uint8_t fruid, fru_area_vec_t & area_vec,
78 sd_bus *bus_type, const bool set_present)
Vishwa4be4b7a2015-10-31 22:55:50 -050079{
80 // Now, use this fru dictionary object and connect with FRU Inventory Dbus
81 // and update the data for this FRU ID.
82 int rc = 0;
vishwaf3ca3522015-12-02 10:35:13 -060083
Vishwa4be4b7a2015-10-31 22:55:50 -050084 // Dictionary object to hold Name:Value pair
85 sd_bus_message *fru_dict = NULL;
86
87 // SD Bus error report mechanism.
88 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
89
Vishwa4be4b7a2015-10-31 22:55:50 -050090 // Req message contains the specifics about which method etc that we want to
91 // access on which bus, object
92 sd_bus_message *response = NULL;
93
Vishwa4be4b7a2015-10-31 22:55:50 -050094 // For each FRU area, extract the needed data , get it parsed and update
95 // the Inventory.
96 for(auto& iter : area_vec)
97 {
98 uint8_t area_type = (iter).type;
99
100 uint8_t area_data[(iter).len];
101 memset(area_data, 0x0, sizeof(area_data));
102
103 // Grab area specific data
104 memmove(area_data, (iter).offset, (iter).len);
105
106 // Need this to get respective DBUS objects
107 const char *area_name = NULL;
108
109 if(area_type == IPMI_FRU_AREA_CHASSIS_INFO)
110 {
111 area_name = "CHASSIS_";
112 }
113 else if(area_type == IPMI_FRU_AREA_BOARD_INFO)
114 {
115 area_name = "BOARD_";
116 }
117 else if(area_type == IPMI_FRU_AREA_PRODUCT_INFO)
118 {
119 area_name = "PRODUCT_";
120 }
121 else
122 {
123 fprintf(stderr, "ERROR: Invalid Area type :[%d]",area_type);
124 break;
125 }
vishwaf3ca3522015-12-02 10:35:13 -0600126
Vishwa4be4b7a2015-10-31 22:55:50 -0500127 // What we need is BOARD_1, PRODUCT_1, CHASSIS_1 etc..
128 char fru_area_name[16] = {0};
129 sprintf(fru_area_name,"%s%d",area_name, fruid);
130
131#ifdef __IPMI_DEBUG__
132 printf("Updating Inventory with :[%s]\n",fru_area_name);
133#endif
vishwaf3ca3522015-12-02 10:35:13 -0600134 // Each area needs a clean set.
Vishwa4be4b7a2015-10-31 22:55:50 -0500135 sd_bus_error_free(&bus_error);
136 sd_bus_message_unref(response);
137 sd_bus_message_unref(fru_dict);
vishwaf3ca3522015-12-02 10:35:13 -0600138
Vishwa4be4b7a2015-10-31 22:55:50 -0500139 // We want to call a method "getObjectFromId" on System Bus that is
140 // made available over OpenBmc system services.
141 rc = sd_bus_call_method(bus_type, // On the System Bus
vishwa13555bd2015-11-10 12:10:38 -0600142 sys_bus_name, // Service to contact
vishwaf3ca3522015-12-02 10:35:13 -0600143 sys_object_name, // Object path
vishwa13555bd2015-11-10 12:10:38 -0600144 sys_intf_name, // Interface name
Vishwa4be4b7a2015-10-31 22:55:50 -0500145 "getObjectFromId", // Method to be called
146 &bus_error, // object to return error
147 &response, // Response message on success
148 "ss", // input message (string,byte)
149 "FRU_STR", // First argument to getObjectFromId
150 fru_area_name); // Second Argument
151
152 if(rc < 0)
153 {
vishwaf3ca3522015-12-02 10:35:13 -0600154 fprintf(stderr, "Failed to resolve fruid to dbus: %s\n", bus_error.message);
Vishwa4be4b7a2015-10-31 22:55:50 -0500155 break;
156 }
157
158 // Method getObjectFromId returns 3 parameters and all are strings, namely
vishwaf3ca3522015-12-02 10:35:13 -0600159 // bus_name , object_path and interface name for accessing that particular
Vishwa4be4b7a2015-10-31 22:55:50 -0500160 // FRU over Inventory SDBUS manager. 'sss' here mentions that format.
161 char *inv_bus_name, *inv_obj_path, *inv_intf_name;
162 rc = sd_bus_message_read(response, "(sss)", &inv_bus_name, &inv_obj_path, &inv_intf_name);
163 if(rc < 0)
164 {
165 fprintf(stderr, "Failed to parse response message:[%s]\n", strerror(-rc));
166 break;
167 }
168
169#ifdef __IPMI_DEBUG__
170 printf("fru_area=[%s], inv_bus_name=[%s], inv_obj_path=[%s],inv_intf_name=[%s]\n",
171 fru_area_name, inv_bus_name, inv_obj_path, inv_intf_name);
172#endif
173
174 // Constructor to allow further initializations and customization.
175 rc = sd_bus_message_new_method_call(bus_type,
176 &fru_dict,
177 inv_bus_name,
178 inv_obj_path,
179 inv_intf_name,
180 "update");
181 if(rc < 0)
182 {
183 fprintf(stderr,"ERROR: creating a update method call\n");
184 break;
185 }
186
187 // A Dictionary ({}) having (string, variant)
188 rc = sd_bus_message_open_container(fru_dict, 'a', "{sv}");
189 if(rc < 0)
190 {
191 fprintf(stderr,"ERROR:[%d] creating a dict container:\n",errno);
192 break;
193 }
194
195 // Fill the container with information
196 rc = parse_fru_area((iter).type, (void *)area_data, (iter).len, fru_dict);
197 if(rc < 0)
198 {
199 fprintf(stderr,"ERROR parsing FRU records\n");
200 break;
201 }
202
203 sd_bus_message_close_container(fru_dict);
204
205 // Now, Make the actual call to update the FRU inventory database with the
206 // dictionary given by FRU Parser. There is no response message expected for
207 // this.
208 rc = sd_bus_call(bus_type, // On the System Bus
209 fru_dict, // With the Name:value dictionary array
vishwaf3ca3522015-12-02 10:35:13 -0600210 0, //
Vishwa4be4b7a2015-10-31 22:55:50 -0500211 &bus_error, // Object to return error.
212 &response); // Response message if any.
213
214 if(rc < 0)
215 {
216 fprintf(stderr, "ERROR:[%s] updating FRU inventory for ID:[0x%X]\n",
217 bus_error.message, fruid);
218 }
vishwaf3ca3522015-12-02 10:35:13 -0600219 else if(set_present)
220 {
221 printf("SUCCESS: Updated:[%s] successfully. Setting Present status now\n",fru_area_name);
222
223 // Clear any old residue
224 sd_bus_error_free(&bus_error);
225 sd_bus_message_unref(response);
226
227 // If we are asked to set the present status. do it.
228 rc = sd_bus_call_method(bus_type, // On the System Bus
229 inv_bus_name, // Service to contact
230 inv_obj_path, // Object path
231 inv_intf_name, // Interface name
232 "setPresent", // Method to be called
233 &bus_error, // object to return error
234 &response, // Response message on success
235 "s", // input message (string)
236 "True"); // First argument to getObjectFromId
237
238 if(rc < 0)
239 {
240 fprintf(stderr, "Failed to update Present status: %s\n", bus_error.message);
241 break;
242 }
243 }
Vishwa4be4b7a2015-10-31 22:55:50 -0500244 else
245 {
246 printf("SUCCESS: Updated:[%s] successfully\n",fru_area_name);
247 }
248 } // END walking the vector of areas and updating
249
250 sd_bus_error_free(&bus_error);
251 sd_bus_message_unref(response);
252 sd_bus_message_unref(fru_dict);
253 sd_bus_unref(bus_type);
254
255 return rc;
256}
257
258//-------------------------------------------------------------------------
259// Validates the CRC and if found good, calls fru areas parser and calls
vishwaf3ca3522015-12-02 10:35:13 -0600260// Inventory Dbus with the dictionary of Name:Value for updating.
Vishwa4be4b7a2015-10-31 22:55:50 -0500261//-------------------------------------------------------------------------
vishwaf3ca3522015-12-02 10:35:13 -0600262int ipmi_validate_and_update_inventory(const uint8_t fruid, const uint8_t *fru_data,
263 sd_bus *bus_type, const bool set_present)
Vishwa4be4b7a2015-10-31 22:55:50 -0500264{
265 // Used for generic checksum calculation
266 uint8_t checksum = 0;
267
268 // This can point to any FRU entry.
269 uint8_t fru_entry;
270
271 // A generic offset locator for any FRU record.
vishwa13555bd2015-11-10 12:10:38 -0600272 size_t area_offset = 0;
Vishwa4be4b7a2015-10-31 22:55:50 -0500273
274 // First 2 bytes in the record.
275 uint8_t fru_area_hdr[2] = {0};
276
277 // To hold info about individual FRU record areas.
278 fru_area_t fru_area;
279
280 // For parsing and updating Inventory.
281 fru_area_vec_t fru_area_vec;
282
283 int rc = 0;
284
285 uint8_t common_hdr[sizeof(struct common_header)] = {0};
286 memset(common_hdr, 0x0, sizeof(common_hdr));
287
288 // Copy first 8 bytes to verify common header
289 memcpy(common_hdr, fru_data, sizeof(common_hdr));
290
291 // Validate for first byte to always have a value of [1]
292 if(common_hdr[0] != IPMI_FRU_HDR_BYTE_ZERO)
293 {
vishwaf3ca3522015-12-02 10:35:13 -0600294 fprintf(stderr, "Invalid Common Header entry_1:[0x%X]\n",common_hdr[0]);
Vishwa4be4b7a2015-10-31 22:55:50 -0500295 return -1;
296 }
297 else
298 {
299 printf("SUCCESS: Validated [0x%X] in common header\n",common_hdr[0]);
300 }
301
302 // Validate the header checskum that is at last byte ( Offset: 7 )
303 checksum = calculate_crc(common_hdr, sizeof(common_hdr)-1);
304 if(checksum != common_hdr[IPMI_FRU_HDR_CRC_OFFSET])
305 {
306#ifdef __IPMI__DEBUG__
vishwaf3ca3522015-12-02 10:35:13 -0600307 fprintf(stderr, "Common Header checksum mismatch."
308 " Calculated:[0x%X], Embedded:[0x%X]\n",
Vishwa4be4b7a2015-10-31 22:55:50 -0500309 checksum, common_hdr[IPMI_FRU_HDR_CRC_OFFSET]);
vishwaf3ca3522015-12-02 10:35:13 -0600310#endif
Vishwa4be4b7a2015-10-31 22:55:50 -0500311 return -1;
312 }
313 else
314 {
315 printf("SUCCESS: Common Header checksum MATCH:[0x%X]\n",checksum);
316 }
317
318 //-------------------------------------------
319 // TODO: Add support for Multi Record later
320 //-------------------------------------------
321
322 // Now start walking the common_hdr array that has offsets into other FRU
323 // record areas and validate those. Starting with second entry since the
324 // first one is always a [0x01]
325 for(fru_entry = IPMI_FRU_INTERNAL_OFFSET; fru_entry < (sizeof(struct common_header) -2); fru_entry++)
326 {
327 // Offset is 'value given in' internal_offset * 8 from the START of
328 // common header. So an an example, 01 00 00 00 01 00 00 fe has
329 // product area set at the offset 01 * 8 --> 8 bytes from the START of
330 // common header. That means, soon after the header checksum.
331 area_offset = common_hdr[fru_entry] * IPMI_EIGHT_BYTES;
vishwaf3ca3522015-12-02 10:35:13 -0600332
Vishwa4be4b7a2015-10-31 22:55:50 -0500333 if(area_offset)
334 {
335 memset((void *)&fru_area, 0x0, sizeof(fru_area_t));
336
337 // Enumerated FRU area.
338 fru_area.type = get_fru_area_type(fru_entry);
339
340 // From start of fru header + record offset, copy 2 bytes.
341 fru_area.offset = &((uint8_t *)fru_data)[area_offset];
342 memcpy(fru_area_hdr, fru_area.offset, sizeof(fru_area_hdr));
343
344 // A NON zero value means that the vpd packet has the data for that
345 // area. err if first element in the record header is _not_ a [0x01].
346 if(fru_area_hdr[0] != IPMI_FRU_HDR_BYTE_ZERO)
347 {
vishwaf3ca3522015-12-02 10:35:13 -0600348 fprintf(stderr, "Unexpected :[0x%X] found at Record header\n",
Vishwa4be4b7a2015-10-31 22:55:50 -0500349 fru_area_hdr[0]);
350
351 // This vector by now may have had some entries. Since this is a
352 // failure now, clear the state data.
353 fru_area_vec.clear();
354 return -1;
355 }
356 else
357 {
358 printf("SUCCESS: Validated [0x%X] in fru record:[%d] header\n",
359 fru_area_hdr[0],fru_entry);
360 }
361
362 // Read Length bytes ( makes a complete record read now )
363 fru_area.len = fru_area_hdr[1] * IPMI_EIGHT_BYTES;
364#ifdef __IPMI_DEBUG__
365 printf("AREA NO[%d], SIZE = [%d]\n",fru_entry, fru_area.len);
366#endif
367 uint8_t fru_area_data[fru_area.len];
368 memset(fru_area_data, 0x0, sizeof(fru_area_data));
369
370 memmove(fru_area_data, fru_area.offset, sizeof(fru_area_data));
371
372 // Calculate checksum (from offset -> (Length-1)).
373 // All the bytes except the last byte( which is CRC :) ) will
374 // participate in calculating the checksum.
375 checksum = calculate_crc(fru_area_data, sizeof(fru_area_data)-1);
376
377 // Verify the embedded checksum in last byte with calculated checksum
378 // record_len -1 since length is some N but numbering is 0..N-1
379 if(checksum != fru_area_data[fru_area.len-1])
380 {
381#ifdef __IPMI_DEBUG__
vishwaf3ca3522015-12-02 10:35:13 -0600382 fprintf(stderr, "FRU Header checksum mismatch. "
383 " Calculated:[0x%X], Embedded:[0x%X]\n",
Vishwa4be4b7a2015-10-31 22:55:50 -0500384 checksum, fru_area_data[fru_area.len - 1]);
385#endif
386 // This vector by now may have had some entries. Since this is a
387 // failure now, clear the state data.
388 fru_area_vec.clear();
389 return -1;
390 }
391 else
392 {
393 printf("SUCCESS: FRU Header checksum MATCH:[0x%X]\n",checksum);
394 }
395
396 // Everything is rihgt about this particular FRU record,
397 fru_area_vec.push_back(fru_area);
398
399 // Update the internal structure with info about this entry that is
400 // needed while handling each areas.
401 } // If the packet has data for a particular data record.
402 } // End walking all the fru records.
vishwaf3ca3522015-12-02 10:35:13 -0600403
Vishwa4be4b7a2015-10-31 22:55:50 -0500404 // If we reach here, then we have validated the crc for all the records and
405 // time to call FRU area parser to get a Name:Value pair dictionary.
406 // This will start iterating all over again on the buffer -BUT- now with the
407 // job of taking each areas, getting it parsed and then updating the
408 // DBUS.
vishwaf3ca3522015-12-02 10:35:13 -0600409
Vishwa4be4b7a2015-10-31 22:55:50 -0500410 if(!(fru_area_vec.empty()))
411 {
vishwaf3ca3522015-12-02 10:35:13 -0600412 rc = ipmi_update_inventory(fruid, fru_area_vec, bus_type, set_present);
Vishwa4be4b7a2015-10-31 22:55:50 -0500413 }
vishwaf3ca3522015-12-02 10:35:13 -0600414
415 // We are done with this FRU write packet.
Vishwa4be4b7a2015-10-31 22:55:50 -0500416 fru_area_vec.clear();
417
418 return rc;
419}
420
421///-----------------------------------------------------
422// Accepts the filename and validates per IPMI FRU spec
423//----------------------------------------------------
vishwaf3ca3522015-12-02 10:35:13 -0600424int ipmi_validate_fru_area(const uint8_t fruid, const char *fru_file_name,
425 sd_bus *bus_type, const bool set_present)
Vishwa4be4b7a2015-10-31 22:55:50 -0500426{
427 int file_size = 0;
428 uint8_t *fru_data = NULL;
429 int bytes_read = 0;
430 int rc = 0;
431
432 FILE *fru_file = fopen(fru_file_name,"rb");
433 if(fru_file == NULL)
434 {
435 fprintf(stderr, "ERROR: opening:[%s]\n",fru_file_name);
436 perror("Error:");
437 return -1;
438 }
439
440 // Get the size of the file to allocate buffer to hold the entire contents.
441 if(fseek(fru_file, 0, SEEK_END))
442 {
443 perror("Error:");
444 fclose(fru_file);
445 return -1;
446 }
447
448 file_size = ftell(fru_file);
449 fru_data = (uint8_t *)malloc(file_size);
450
451 // Read entire file contents to the internal buffer
452 if(fseek(fru_file, 0, SEEK_SET))
453 {
454 perror("Error:");
455 fclose(fru_file);
456 return -1;
457 }
458
459 bytes_read = fread(fru_data, file_size, 1, fru_file);
460 if(bytes_read != 1)
461 {
vishwaf3ca3522015-12-02 10:35:13 -0600462 fprintf(stderr, "failed reading common header. Bytes read=:[%d]\n",bytes_read);
Vishwa4be4b7a2015-10-31 22:55:50 -0500463 perror("Error:");
464 fclose(fru_file);
465 return -1;
466 }
467 fclose(fru_file);
468
vishwaf3ca3522015-12-02 10:35:13 -0600469 rc = ipmi_validate_and_update_inventory(fruid, fru_data, bus_type, set_present);
470 if(rc < 0)
Vishwa4be4b7a2015-10-31 22:55:50 -0500471 {
vishwaf3ca3522015-12-02 10:35:13 -0600472 fprintf(stderr,"Validation failed for:[%d]\n",fruid);
Vishwa4be4b7a2015-10-31 22:55:50 -0500473 }
474 else
475 {
476 printf("SUCCESS: Validated:[%s]\n",fru_file_name);
477 }
478
479 if(fru_data)
480 {
481 free(fru_data);
482 fru_data = NULL;
483 }
484
485 return rc;
486}
487