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