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