blob: ae0d1eb961a2f288729dd93cf8cdf6b94cd25314 [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//------------------------------------------------
21// Takes the pointer to stream of bytes and length
22// 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 }
33
34 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//------------------------------------------------------------------------
77int ipmi_update_inventory(const uint8_t fruid, const uint8_t *fru_data,
vishwa13555bd2015-11-10 12:10:38 -060078 fru_area_vec_t & area_vec, sd_bus *bus_type)
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;
83
84 // 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 }
126
127 // 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
134 // Each area needs a clean set.
135 sd_bus_error_free(&bus_error);
136 sd_bus_message_unref(response);
137 sd_bus_message_unref(fru_dict);
138
139 // 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
143 sys_object_name, // Object path
144 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 {
154 fprintf(stderr, "Failed to issue method call: %s\n", bus_error.message);
155 break;
156 }
157
158 // Method getObjectFromId returns 3 parameters and all are strings, namely
159 // bus_name , object_path and interface name for accessing that particular
160 // 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
210 0, //
211 &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 }
219 else
220 {
221 printf("SUCCESS: Updated:[%s] successfully\n",fru_area_name);
222 }
223 } // END walking the vector of areas and updating
224
225 sd_bus_error_free(&bus_error);
226 sd_bus_message_unref(response);
227 sd_bus_message_unref(fru_dict);
228 sd_bus_unref(bus_type);
229
230 return rc;
231}
232
233//-------------------------------------------------------------------------
234// Validates the CRC and if found good, calls fru areas parser and calls
235// Inventory Dbus with the dictionary of Name:Value for updating.
236//-------------------------------------------------------------------------
vishwa13555bd2015-11-10 12:10:38 -0600237int ipmi_validate_and_update_inventory(const uint8_t fruid, const uint8_t *fru_data,
238 sd_bus *bus_type)
Vishwa4be4b7a2015-10-31 22:55:50 -0500239{
240 // Used for generic checksum calculation
241 uint8_t checksum = 0;
242
243 // This can point to any FRU entry.
244 uint8_t fru_entry;
245
246 // A generic offset locator for any FRU record.
vishwa13555bd2015-11-10 12:10:38 -0600247 size_t area_offset = 0;
Vishwa4be4b7a2015-10-31 22:55:50 -0500248
249 // First 2 bytes in the record.
250 uint8_t fru_area_hdr[2] = {0};
251
252 // To hold info about individual FRU record areas.
253 fru_area_t fru_area;
254
255 // For parsing and updating Inventory.
256 fru_area_vec_t fru_area_vec;
257
258 int rc = 0;
259
260 uint8_t common_hdr[sizeof(struct common_header)] = {0};
261 memset(common_hdr, 0x0, sizeof(common_hdr));
262
263 // Copy first 8 bytes to verify common header
264 memcpy(common_hdr, fru_data, sizeof(common_hdr));
265
266 // Validate for first byte to always have a value of [1]
267 if(common_hdr[0] != IPMI_FRU_HDR_BYTE_ZERO)
268 {
269 fprintf(stderr, "ERROR: Common Header entry_1:[0x%X] Invalid.\n",common_hdr[0]);
270 return -1;
271 }
272 else
273 {
274 printf("SUCCESS: Validated [0x%X] in common header\n",common_hdr[0]);
275 }
276
277 // Validate the header checskum that is at last byte ( Offset: 7 )
278 checksum = calculate_crc(common_hdr, sizeof(common_hdr)-1);
279 if(checksum != common_hdr[IPMI_FRU_HDR_CRC_OFFSET])
280 {
281#ifdef __IPMI__DEBUG__
282 fprintf(stderr, "ERROR: Common Header checksum mismatch."
283 " Calculated:[0x%X], Embedded:[0x%X]\n",
284 checksum, common_hdr[IPMI_FRU_HDR_CRC_OFFSET]);
285#endif
286 return -1;
287 }
288 else
289 {
290 printf("SUCCESS: Common Header checksum MATCH:[0x%X]\n",checksum);
291 }
292
293 //-------------------------------------------
294 // TODO: Add support for Multi Record later
295 //-------------------------------------------
296
297 // Now start walking the common_hdr array that has offsets into other FRU
298 // record areas and validate those. Starting with second entry since the
299 // first one is always a [0x01]
300 for(fru_entry = IPMI_FRU_INTERNAL_OFFSET; fru_entry < (sizeof(struct common_header) -2); fru_entry++)
301 {
302 // Offset is 'value given in' internal_offset * 8 from the START of
303 // common header. So an an example, 01 00 00 00 01 00 00 fe has
304 // product area set at the offset 01 * 8 --> 8 bytes from the START of
305 // common header. That means, soon after the header checksum.
306 area_offset = common_hdr[fru_entry] * IPMI_EIGHT_BYTES;
307
308 if(area_offset)
309 {
310 memset((void *)&fru_area, 0x0, sizeof(fru_area_t));
311
312 // Enumerated FRU area.
313 fru_area.type = get_fru_area_type(fru_entry);
314
315 // From start of fru header + record offset, copy 2 bytes.
316 fru_area.offset = &((uint8_t *)fru_data)[area_offset];
317 memcpy(fru_area_hdr, fru_area.offset, sizeof(fru_area_hdr));
318
319 // A NON zero value means that the vpd packet has the data for that
320 // area. err if first element in the record header is _not_ a [0x01].
321 if(fru_area_hdr[0] != IPMI_FRU_HDR_BYTE_ZERO)
322 {
323 fprintf(stderr, "ERROR: Unexpected :[0x%X] found at Record header\n",
324 fru_area_hdr[0]);
325
326 // This vector by now may have had some entries. Since this is a
327 // failure now, clear the state data.
328 fru_area_vec.clear();
329 return -1;
330 }
331 else
332 {
333 printf("SUCCESS: Validated [0x%X] in fru record:[%d] header\n",
334 fru_area_hdr[0],fru_entry);
335 }
336
337 // Read Length bytes ( makes a complete record read now )
338 fru_area.len = fru_area_hdr[1] * IPMI_EIGHT_BYTES;
339#ifdef __IPMI_DEBUG__
340 printf("AREA NO[%d], SIZE = [%d]\n",fru_entry, fru_area.len);
341#endif
342 uint8_t fru_area_data[fru_area.len];
343 memset(fru_area_data, 0x0, sizeof(fru_area_data));
344
345 memmove(fru_area_data, fru_area.offset, sizeof(fru_area_data));
346
347 // Calculate checksum (from offset -> (Length-1)).
348 // All the bytes except the last byte( which is CRC :) ) will
349 // participate in calculating the checksum.
350 checksum = calculate_crc(fru_area_data, sizeof(fru_area_data)-1);
351
352 // Verify the embedded checksum in last byte with calculated checksum
353 // record_len -1 since length is some N but numbering is 0..N-1
354 if(checksum != fru_area_data[fru_area.len-1])
355 {
356#ifdef __IPMI_DEBUG__
357 fprintf(stderr, "ERROR: FRU Header checksum mismatch. "
358 " Calculated:[0x%X], Embedded:[0x%X]\n",
359 checksum, fru_area_data[fru_area.len - 1]);
360#endif
361 // This vector by now may have had some entries. Since this is a
362 // failure now, clear the state data.
363 fru_area_vec.clear();
364 return -1;
365 }
366 else
367 {
368 printf("SUCCESS: FRU Header checksum MATCH:[0x%X]\n",checksum);
369 }
370
371 // Everything is rihgt about this particular FRU record,
372 fru_area_vec.push_back(fru_area);
373
374 // Update the internal structure with info about this entry that is
375 // needed while handling each areas.
376 } // If the packet has data for a particular data record.
377 } // End walking all the fru records.
378
379 // If we reach here, then we have validated the crc for all the records and
380 // time to call FRU area parser to get a Name:Value pair dictionary.
381 // This will start iterating all over again on the buffer -BUT- now with the
382 // job of taking each areas, getting it parsed and then updating the
383 // DBUS.
384
385 if(!(fru_area_vec.empty()))
386 {
vishwa13555bd2015-11-10 12:10:38 -0600387 rc = ipmi_update_inventory(fruid, fru_data, fru_area_vec, bus_type);
Vishwa4be4b7a2015-10-31 22:55:50 -0500388 }
389
390 // We are done with this FRU write packet.
391 fru_area_vec.clear();
392
393 return rc;
394}
395
396///-----------------------------------------------------
397// Accepts the filename and validates per IPMI FRU spec
398//----------------------------------------------------
vishwa13555bd2015-11-10 12:10:38 -0600399int ipmi_validate_fru_area(const uint8_t fruid, const char *fru_file_name,
400 sd_bus *bus_type)
Vishwa4be4b7a2015-10-31 22:55:50 -0500401{
402 int file_size = 0;
403 uint8_t *fru_data = NULL;
404 int bytes_read = 0;
405 int rc = 0;
406
407 FILE *fru_file = fopen(fru_file_name,"rb");
408 if(fru_file == NULL)
409 {
410 fprintf(stderr, "ERROR: opening:[%s]\n",fru_file_name);
411 perror("Error:");
412 return -1;
413 }
414
415 // Get the size of the file to allocate buffer to hold the entire contents.
416 if(fseek(fru_file, 0, SEEK_END))
417 {
418 perror("Error:");
419 fclose(fru_file);
420 return -1;
421 }
422
423 file_size = ftell(fru_file);
424 fru_data = (uint8_t *)malloc(file_size);
425
426 // Read entire file contents to the internal buffer
427 if(fseek(fru_file, 0, SEEK_SET))
428 {
429 perror("Error:");
430 fclose(fru_file);
431 return -1;
432 }
433
434 bytes_read = fread(fru_data, file_size, 1, fru_file);
435 if(bytes_read != 1)
436 {
437 fprintf(stderr, "ERROR reading common header. Bytes read=:[%d]\n",bytes_read);
438 perror("Error:");
439 fclose(fru_file);
440 return -1;
441 }
442 fclose(fru_file);
443
vishwa13555bd2015-11-10 12:10:38 -0600444 rc = ipmi_validate_and_update_inventory(fruid, fru_data, bus_type);
Vishwa4be4b7a2015-10-31 22:55:50 -0500445 if(rc == -1)
446 {
447 printf("ERROR: Validation failed for:[%d]\n",fruid);
448 }
449 else
450 {
451 printf("SUCCESS: Validated:[%s]\n",fru_file_name);
452 }
453
454 if(fru_data)
455 {
456 free(fru_data);
457 fru_data = NULL;
458 }
459
460 return rc;
461}
462