blob: 91aef459647b758be704cc7f0d8c6edc80645806 [file] [log] [blame]
Chris Austenb45c4cb2015-11-01 06:34:56 -06001#include <host-ipmid/ipmid-api.h>
Vishwa4be4b7a2015-10-31 22:55:50 -05002#include <vector>
3#include <stdlib.h>
4#include <dlfcn.h>
5#include <errno.h>
6#include <stdio.h>
7#include "frup.h"
8#include "writefrudata.H"
9#include <systemd/sd-bus.h>
Chris Austenb45c4cb2015-11-01 06:34:56 -060010#include <unistd.h>
11
Vishwa4be4b7a2015-10-31 22:55:50 -050012
13void register_netfn_storage_write_fru() __attribute__((constructor));
14
15// Needed to be passed into fru parser alorithm
16typedef std::vector<fru_area_t> fru_area_vec_t;
17
18// OpenBMC System Manager dbus framework
19const char *bus_name = "org.openbmc.managers.System";
20const char *object_name = "/org/openbmc/managers/System";
21const char *intf_name = "org.openbmc.managers.System";
22
23//------------------------------------------------
24// Takes the pointer to stream of bytes and length
25// returns the 8 bit checksum per IPMI spec.
26//-------------------------------------------------
27unsigned char calculate_crc(unsigned char *data, int len)
28{
29 char crc = 0;
30 int byte = 0;
31
32 for(byte = 0; byte < len; byte++)
33 {
34 crc += *data++;
35 }
36
37 return(-crc);
38}
39
40//---------------------------------------------------------------------
41// Accepts a fru area offset in commom hdr and tells which area it is.
42//---------------------------------------------------------------------
43uint8_t get_fru_area_type(uint8_t area_offset)
44{
45 ipmi_fru_area_type type = IPMI_FRU_AREA_TYPE_MAX;
46
47 switch(area_offset)
48 {
49 case IPMI_FRU_INTERNAL_OFFSET:
50 type = IPMI_FRU_AREA_INTERNAL_USE;
51 break;
52
53 case IPMI_FRU_CHASSIS_OFFSET:
54 type = IPMI_FRU_AREA_CHASSIS_INFO;
55 break;
56
57 case IPMI_FRU_BOARD_OFFSET:
58 type = IPMI_FRU_AREA_BOARD_INFO;
59 break;
60
61 case IPMI_FRU_PRODUCT_OFFSET:
62 type = IPMI_FRU_AREA_PRODUCT_INFO;
63 break;
64
65 case IPMI_FRU_MULTI_OFFSET:
66 type = IPMI_FRU_AREA_MULTI_RECORD;
67 break;
68
69 default:
70 type = IPMI_FRU_AREA_TYPE_MAX;
71 }
72
73 return type;
74}
75
76//------------------------------------------------------------------------
77// Takes FRU data, invokes Parser for each fru record area and updates
78// Inventory
79//------------------------------------------------------------------------
80int ipmi_update_inventory(const uint8_t fruid, const uint8_t *fru_data,
81 fru_area_vec_t & area_vec)
82{
83 // Now, use this fru dictionary object and connect with FRU Inventory Dbus
84 // and update the data for this FRU ID.
85 int rc = 0;
86
87 // Dictionary object to hold Name:Value pair
88 sd_bus_message *fru_dict = NULL;
89
90 // SD Bus error report mechanism.
91 sd_bus_error bus_error = SD_BUS_ERROR_NULL;
92
93 // Gets a hook onto either a SYSTEM or SESSION bus
Chris Austen78d46212015-11-13 14:46:27 -060094 sd_bus *bus_type = ipmid_get_sd_bus_connection();
Vishwa4be4b7a2015-10-31 22:55:50 -050095
96 // Req message contains the specifics about which method etc that we want to
97 // access on which bus, object
98 sd_bus_message *response = NULL;
99
Vishwa4be4b7a2015-10-31 22:55:50 -0500100 // For each FRU area, extract the needed data , get it parsed and update
101 // the Inventory.
102 for(auto& iter : area_vec)
103 {
104 uint8_t area_type = (iter).type;
105
106 uint8_t area_data[(iter).len];
107 memset(area_data, 0x0, sizeof(area_data));
108
109 // Grab area specific data
110 memmove(area_data, (iter).offset, (iter).len);
111
112 // Need this to get respective DBUS objects
113 const char *area_name = NULL;
114
115 if(area_type == IPMI_FRU_AREA_CHASSIS_INFO)
116 {
117 area_name = "CHASSIS_";
118 }
119 else if(area_type == IPMI_FRU_AREA_BOARD_INFO)
120 {
121 area_name = "BOARD_";
122 }
123 else if(area_type == IPMI_FRU_AREA_PRODUCT_INFO)
124 {
125 area_name = "PRODUCT_";
126 }
127 else
128 {
129 fprintf(stderr, "ERROR: Invalid Area type :[%d]",area_type);
130 break;
131 }
132
133 // What we need is BOARD_1, PRODUCT_1, CHASSIS_1 etc..
134 char fru_area_name[16] = {0};
135 sprintf(fru_area_name,"%s%d",area_name, fruid);
136
137#ifdef __IPMI_DEBUG__
138 printf("Updating Inventory with :[%s]\n",fru_area_name);
139#endif
140 // Each area needs a clean set.
141 sd_bus_error_free(&bus_error);
142 sd_bus_message_unref(response);
143 sd_bus_message_unref(fru_dict);
144
145 // We want to call a method "getObjectFromId" on System Bus that is
146 // made available over OpenBmc system services.
147 rc = sd_bus_call_method(bus_type, // On the System Bus
148 bus_name, // Service to contact
149 object_name, // Object path
150 intf_name, // Interface name
151 "getObjectFromId", // Method to be called
152 &bus_error, // object to return error
153 &response, // Response message on success
154 "ss", // input message (string,byte)
155 "FRU_STR", // First argument to getObjectFromId
156 fru_area_name); // Second Argument
157
158 if(rc < 0)
159 {
160 fprintf(stderr, "Failed to issue method call: %s\n", bus_error.message);
161 break;
162 }
163
164 // Method getObjectFromId returns 3 parameters and all are strings, namely
165 // bus_name , object_path and interface name for accessing that particular
166 // FRU over Inventory SDBUS manager. 'sss' here mentions that format.
167 char *inv_bus_name, *inv_obj_path, *inv_intf_name;
168 rc = sd_bus_message_read(response, "(sss)", &inv_bus_name, &inv_obj_path, &inv_intf_name);
169 if(rc < 0)
170 {
171 fprintf(stderr, "Failed to parse response message:[%s]\n", strerror(-rc));
172 break;
173 }
174
175#ifdef __IPMI_DEBUG__
176 printf("fru_area=[%s], inv_bus_name=[%s], inv_obj_path=[%s],inv_intf_name=[%s]\n",
177 fru_area_name, inv_bus_name, inv_obj_path, inv_intf_name);
178#endif
179
180 // Constructor to allow further initializations and customization.
181 rc = sd_bus_message_new_method_call(bus_type,
182 &fru_dict,
183 inv_bus_name,
184 inv_obj_path,
185 inv_intf_name,
186 "update");
187 if(rc < 0)
188 {
189 fprintf(stderr,"ERROR: creating a update method call\n");
190 break;
191 }
192
193 // A Dictionary ({}) having (string, variant)
194 rc = sd_bus_message_open_container(fru_dict, 'a', "{sv}");
195 if(rc < 0)
196 {
197 fprintf(stderr,"ERROR:[%d] creating a dict container:\n",errno);
198 break;
199 }
200
201 // Fill the container with information
202 rc = parse_fru_area((iter).type, (void *)area_data, (iter).len, fru_dict);
203 if(rc < 0)
204 {
205 fprintf(stderr,"ERROR parsing FRU records\n");
206 break;
207 }
208
209 sd_bus_message_close_container(fru_dict);
210
211 // Now, Make the actual call to update the FRU inventory database with the
212 // dictionary given by FRU Parser. There is no response message expected for
213 // this.
214 rc = sd_bus_call(bus_type, // On the System Bus
215 fru_dict, // With the Name:value dictionary array
216 0, //
217 &bus_error, // Object to return error.
218 &response); // Response message if any.
219
220 if(rc < 0)
221 {
222 fprintf(stderr, "ERROR:[%s] updating FRU inventory for ID:[0x%X]\n",
223 bus_error.message, fruid);
224 }
225 else
226 {
227 printf("SUCCESS: Updated:[%s] successfully\n",fru_area_name);
228 }
229 } // END walking the vector of areas and updating
230
231 sd_bus_error_free(&bus_error);
232 sd_bus_message_unref(response);
233 sd_bus_message_unref(fru_dict);
234 sd_bus_unref(bus_type);
235
236 return rc;
237}
238
239//-------------------------------------------------------------------------
240// Validates the CRC and if found good, calls fru areas parser and calls
241// Inventory Dbus with the dictionary of Name:Value for updating.
242//-------------------------------------------------------------------------
243int ipmi_validate_and_update_inventory(const uint8_t fruid, const uint8_t *fru_data)
244{
245 // Used for generic checksum calculation
246 uint8_t checksum = 0;
247
248 // This can point to any FRU entry.
249 uint8_t fru_entry;
250
251 // A generic offset locator for any FRU record.
252 uint8_t area_offset = 0;
253
254 // First 2 bytes in the record.
255 uint8_t fru_area_hdr[2] = {0};
256
257 // To hold info about individual FRU record areas.
258 fru_area_t fru_area;
259
260 // For parsing and updating Inventory.
261 fru_area_vec_t fru_area_vec;
262
263 int rc = 0;
264
265 uint8_t common_hdr[sizeof(struct common_header)] = {0};
266 memset(common_hdr, 0x0, sizeof(common_hdr));
267
268 // Copy first 8 bytes to verify common header
269 memcpy(common_hdr, fru_data, sizeof(common_hdr));
270
271 // Validate for first byte to always have a value of [1]
272 if(common_hdr[0] != IPMI_FRU_HDR_BYTE_ZERO)
273 {
274 fprintf(stderr, "ERROR: Common Header entry_1:[0x%X] Invalid.\n",common_hdr[0]);
275 return -1;
276 }
277 else
278 {
279 printf("SUCCESS: Validated [0x%X] in common header\n",common_hdr[0]);
280 }
281
282 // Validate the header checskum that is at last byte ( Offset: 7 )
283 checksum = calculate_crc(common_hdr, sizeof(common_hdr)-1);
284 if(checksum != common_hdr[IPMI_FRU_HDR_CRC_OFFSET])
285 {
286#ifdef __IPMI__DEBUG__
287 fprintf(stderr, "ERROR: Common Header checksum mismatch."
288 " Calculated:[0x%X], Embedded:[0x%X]\n",
289 checksum, common_hdr[IPMI_FRU_HDR_CRC_OFFSET]);
290#endif
291 return -1;
292 }
293 else
294 {
295 printf("SUCCESS: Common Header checksum MATCH:[0x%X]\n",checksum);
296 }
297
298 //-------------------------------------------
299 // TODO: Add support for Multi Record later
300 //-------------------------------------------
301
302 // Now start walking the common_hdr array that has offsets into other FRU
303 // record areas and validate those. Starting with second entry since the
304 // first one is always a [0x01]
305 for(fru_entry = IPMI_FRU_INTERNAL_OFFSET; fru_entry < (sizeof(struct common_header) -2); fru_entry++)
306 {
307 // Offset is 'value given in' internal_offset * 8 from the START of
308 // common header. So an an example, 01 00 00 00 01 00 00 fe has
309 // product area set at the offset 01 * 8 --> 8 bytes from the START of
310 // common header. That means, soon after the header checksum.
311 area_offset = common_hdr[fru_entry] * IPMI_EIGHT_BYTES;
312
313 if(area_offset)
314 {
315 memset((void *)&fru_area, 0x0, sizeof(fru_area_t));
316
317 // Enumerated FRU area.
318 fru_area.type = get_fru_area_type(fru_entry);
319
320 // From start of fru header + record offset, copy 2 bytes.
321 fru_area.offset = &((uint8_t *)fru_data)[area_offset];
322 memcpy(fru_area_hdr, fru_area.offset, sizeof(fru_area_hdr));
323
324 // A NON zero value means that the vpd packet has the data for that
325 // area. err if first element in the record header is _not_ a [0x01].
326 if(fru_area_hdr[0] != IPMI_FRU_HDR_BYTE_ZERO)
327 {
328 fprintf(stderr, "ERROR: Unexpected :[0x%X] found at Record header\n",
329 fru_area_hdr[0]);
330
331 // This vector by now may have had some entries. Since this is a
332 // failure now, clear the state data.
333 fru_area_vec.clear();
334 return -1;
335 }
336 else
337 {
338 printf("SUCCESS: Validated [0x%X] in fru record:[%d] header\n",
339 fru_area_hdr[0],fru_entry);
340 }
341
342 // Read Length bytes ( makes a complete record read now )
343 fru_area.len = fru_area_hdr[1] * IPMI_EIGHT_BYTES;
344#ifdef __IPMI_DEBUG__
345 printf("AREA NO[%d], SIZE = [%d]\n",fru_entry, fru_area.len);
346#endif
347 uint8_t fru_area_data[fru_area.len];
348 memset(fru_area_data, 0x0, sizeof(fru_area_data));
349
350 memmove(fru_area_data, fru_area.offset, sizeof(fru_area_data));
351
352 // Calculate checksum (from offset -> (Length-1)).
353 // All the bytes except the last byte( which is CRC :) ) will
354 // participate in calculating the checksum.
355 checksum = calculate_crc(fru_area_data, sizeof(fru_area_data)-1);
356
357 // Verify the embedded checksum in last byte with calculated checksum
358 // record_len -1 since length is some N but numbering is 0..N-1
359 if(checksum != fru_area_data[fru_area.len-1])
360 {
361#ifdef __IPMI_DEBUG__
362 fprintf(stderr, "ERROR: FRU Header checksum mismatch. "
363 " Calculated:[0x%X], Embedded:[0x%X]\n",
364 checksum, fru_area_data[fru_area.len - 1]);
365#endif
366 // This vector by now may have had some entries. Since this is a
367 // failure now, clear the state data.
368 fru_area_vec.clear();
369 return -1;
370 }
371 else
372 {
373 printf("SUCCESS: FRU Header checksum MATCH:[0x%X]\n",checksum);
374 }
375
376 // Everything is rihgt about this particular FRU record,
377 fru_area_vec.push_back(fru_area);
378
379 // Update the internal structure with info about this entry that is
380 // needed while handling each areas.
381 } // If the packet has data for a particular data record.
382 } // End walking all the fru records.
383
384 // If we reach here, then we have validated the crc for all the records and
385 // time to call FRU area parser to get a Name:Value pair dictionary.
386 // This will start iterating all over again on the buffer -BUT- now with the
387 // job of taking each areas, getting it parsed and then updating the
388 // DBUS.
389
390 if(!(fru_area_vec.empty()))
391 {
392 rc = ipmi_update_inventory(fruid, fru_data, fru_area_vec);
393 }
394
395 // We are done with this FRU write packet.
396 fru_area_vec.clear();
397
398 return rc;
399}
400
401///-----------------------------------------------------
402// Accepts the filename and validates per IPMI FRU spec
403//----------------------------------------------------
404int ipmi_validate_fru_area(const uint8_t fruid, const char *fru_file_name)
405{
406 int file_size = 0;
407 uint8_t *fru_data = NULL;
408 int bytes_read = 0;
409 int rc = 0;
410
411 FILE *fru_file = fopen(fru_file_name,"rb");
412 if(fru_file == NULL)
413 {
414 fprintf(stderr, "ERROR: opening:[%s]\n",fru_file_name);
415 perror("Error:");
416 return -1;
417 }
418
419 // Get the size of the file to allocate buffer to hold the entire contents.
420 if(fseek(fru_file, 0, SEEK_END))
421 {
422 perror("Error:");
423 fclose(fru_file);
424 return -1;
425 }
426
427 file_size = ftell(fru_file);
428 fru_data = (uint8_t *)malloc(file_size);
429
430 // Read entire file contents to the internal buffer
431 if(fseek(fru_file, 0, SEEK_SET))
432 {
433 perror("Error:");
434 fclose(fru_file);
435 return -1;
436 }
437
438 bytes_read = fread(fru_data, file_size, 1, fru_file);
439 if(bytes_read != 1)
440 {
441 fprintf(stderr, "ERROR reading common header. Bytes read=:[%d]\n",bytes_read);
442 perror("Error:");
443 fclose(fru_file);
444 return -1;
445 }
446 fclose(fru_file);
447
448 rc = ipmi_validate_and_update_inventory(fruid, fru_data);
449 if(rc == -1)
450 {
451 printf("ERROR: Validation failed for:[%d]\n",fruid);
452 }
453 else
454 {
455 printf("SUCCESS: Validated:[%s]\n",fru_file_name);
456 }
457
458 if(fru_data)
459 {
460 free(fru_data);
461 fru_data = NULL;
462 }
463
464 return rc;
465}
466
467///-------------------------------------------------------
468// Called by IPMI netfn router for write fru data command
469//--------------------------------------------------------
470ipmi_ret_t ipmi_storage_write_fru_data(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
471 ipmi_request_t request, ipmi_response_t response,
472 ipmi_data_len_t data_len, ipmi_context_t context)
473{
474 FILE *fp = NULL;
475 char fru_file_name[16] = {0};
476 uint8_t offset = 0;
477 uint16_t len = 0;
478 ipmi_ret_t rc = IPMI_CC_INVALID;
Vishwa4be4b7a2015-10-31 22:55:50 -0500479 const char *mode = NULL;
480
481 // From the payload, extract the header that has fruid and the offsets
482 write_fru_data_t *reqptr = (write_fru_data_t*)request;
483
Vishwa4be4b7a2015-10-31 22:55:50 -0500484 // Maintaining a temporary file to pump the data
485 sprintf(fru_file_name, "%s%02x", "/tmp/ipmifru", reqptr->frunum);
486
487 offset = ((uint16_t)reqptr->offsetms) << 8 | reqptr->offsetls;
488
489 // Length is the number of request bytes minus the header itself.
490 // The header contains an extra byte to indicate the start of
491 // the data (so didn't need to worry about word/byte boundaries)
492 // hence the -1...
493 len = ((uint16_t)*data_len) - (sizeof(write_fru_data_t)-1);
Chris Austen67527272015-11-01 01:13:29 -0500494
495 // On error there is no response data for this command.
496 *data_len = 0;
Vishwa4be4b7a2015-10-31 22:55:50 -0500497
498#ifdef __IPMI__DEBUG__
499 printf("IPMI WRITE-FRU-DATA for [%s] Offset = [%d] Length = [%d]\n",
500 fru_file_name, offset, len);
501#endif
502
Chris Austen67527272015-11-01 01:13:29 -0500503
504 if( access( fru_file_name, F_OK ) == -1 ) {
Vishwa4be4b7a2015-10-31 22:55:50 -0500505 mode = "wb";
Chris Austen67527272015-11-01 01:13:29 -0500506 } else {
Vishwa4be4b7a2015-10-31 22:55:50 -0500507 mode = "rb+";
508 }
509
Chris Austen67527272015-11-01 01:13:29 -0500510 if ((fp = fopen(fru_file_name, mode)) != NULL)
Vishwa4be4b7a2015-10-31 22:55:50 -0500511 {
512 if(fseek(fp, offset, SEEK_SET))
513 {
514 perror("Error:");
515 fclose(fp);
516 return rc;
517 }
518
519 if(fwrite(&reqptr->data, len, 1, fp) != 1)
520 {
521 perror("Error:");
522 fclose(fp);
523 return rc;
524 }
525
526 fclose(fp);
527 }
528 else
529 {
530 fprintf(stderr, "Error trying to write to fru file %s\n",fru_file_name);
531 return rc;
532 }
Vishwa4be4b7a2015-10-31 22:55:50 -0500533
Chris Austen67527272015-11-01 01:13:29 -0500534
535 // If we got here then set the resonse byte
536 // to the number of bytes written
537 memcpy(response, &len, 1);
538 *data_len = 1;
539 rc = IPMI_CC_OK;
540
541 // We received some bytes. It may be full or partial. Send a valid
542 // FRU file to the inventory controller on DBus for the correct number
543 ipmi_validate_fru_area(reqptr->frunum, fru_file_name);
Vishwa4be4b7a2015-10-31 22:55:50 -0500544
545 return rc;
546}
547
548void register_netfn_storage_write_fru()
549{
550 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_STORAGE, IPMI_CMD_WRITE_FRU_DATA);
551 ipmi_register_callback(NETFUN_STORAGE, IPMI_CMD_WRITE_FRU_DATA, NULL, ipmi_storage_write_fru_data);
552}