xref: /openbmc/ipmitool/lib/ipmi_fru.c (revision 3b15a7c0)
1 /*
2 * Copyright (c) 2003 Sun Microsystems, Inc.  All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * Redistribution of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * Redistribution in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * Neither the name of Sun Microsystems, Inc. or the names of
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * This software is provided "AS IS," without a warranty of any kind.
20 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
21 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
22 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
23 * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
24 * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
25 * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
26 * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
27 * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
28 * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
29 * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
30 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31 */
32 
33 #include <ipmitool/ipmi.h>
34 #include <ipmitool/log.h>
35 #include <ipmitool/helper.h>
36 #include <ipmitool/ipmi_intf.h>
37 #include <ipmitool/ipmi_fru.h>
38 #include <ipmitool/ipmi_mc.h>
39 #include <ipmitool/ipmi_sdr.h>
40 #include <ipmitool/ipmi_strings.h>  /* IANA id strings */
41 
42 #include <stdlib.h>
43 #include <string.h>
44 #include <time.h>
45 #include <errno.h>
46 
47 #if HAVE_CONFIG_H
48 # include <config.h>
49 #endif
50 
51 #define FRU_MULTIREC_CHUNK_SIZE     (255 + sizeof(struct fru_multirec_header))
52 
53 extern int verbose;
54 
55 static void ipmi_fru_read_to_bin(struct ipmi_intf * intf, char * pFileName, uint8_t fruId);
56 static void ipmi_fru_write_from_bin(struct ipmi_intf * intf, char * pFileName, uint8_t fruId);
57 static int ipmi_fru_upg_ekeying(struct ipmi_intf * intf, char * pFileName, uint8_t fruId);
58 static int ipmi_fru_get_multirec_location_from_fru(struct ipmi_intf * intf, uint8_t fruId,
59 							struct fru_info *pFruInfo, uint32_t * pRetLocation,
60 							uint32_t * pRetSize);
61 static int ipmi_fru_get_multirec_from_file(char * pFileName, uint8_t * pBufArea,
62 						uint32_t size, uint32_t offset);
63 static int ipmi_fru_get_multirec_size_from_file(char * pFileName, uint32_t * pSize, uint32_t * pOffset);
64 int ipmi_fru_get_adjust_size_from_buffer(uint8_t *pBufArea, uint32_t *pSize);
65 static void ipmi_fru_picmg_ext_print(uint8_t * fru_data, int off, int length);
66 
67 static int ipmi_fru_set_field_string(struct ipmi_intf * intf, unsigned
68 						char fruId, uint8_t f_type, uint8_t f_index, char *f_string);
69 static int
70 ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId,
71 											struct fru_info fru, struct fru_header header,
72 											uint8_t f_type, uint8_t f_index, char *f_string);
73 
74 static void
75 fru_area_print_multirec_bloc(struct ipmi_intf * intf, struct fru_info * fru,
76 			uint8_t id, uint32_t offset);
77 int
78 read_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
79 			uint32_t offset, uint32_t length, uint8_t *frubuf);
80 void free_fru_bloc(t_ipmi_fru_bloc *bloc);
81 
82 /* get_fru_area_str  -  Parse FRU area string from raw data
83 *
84 * @data:   raw FRU data
85 * @offset: offset into data for area
86 *
87 * returns pointer to FRU area string
88 */
89 char * get_fru_area_str(uint8_t * data, uint32_t * offset)
90 {
91 	static const char bcd_plus[] = "0123456789 -.:,_";
92 	char * str;
93 	int len, off, size, i, j, k, typecode;
94 	union {
95 		uint32_t bits;
96 		char chars[4];
97 	} u;
98 
99 	size = 0;
100 	off = *offset;
101 
102 	/* bits 6:7 contain format */
103 	typecode = ((data[off] & 0xC0) >> 6);
104 
105 	// printf("Typecode:%i\n", typecode);
106 	/* bits 0:5 contain length */
107 	len = data[off++];
108 	len &= 0x3f;
109 
110 	switch (typecode) {
111 	case 0:           /* 00b: binary/unspecified */
112 		/* hex dump -> 2x length */
113 		size = (len*2);
114 		break;
115 	case 2:           /* 10b: 6-bit ASCII */
116 		/* 4 chars per group of 1-3 bytes */
117 		size = ((((len+2)*4)/3) & ~3);
118 		break;
119 	case 3:           /* 11b: 8-bit ASCII */
120 	case 1:           /* 01b: BCD plus */
121 		/* no length adjustment */
122 		size = len;
123 		break;
124 	}
125 
126 	if (size < 1) {
127 		*offset = off;
128 		return NULL;
129 	}
130 	str = malloc(size+1);
131 	if (str == NULL)
132 		return NULL;
133 	memset(str, 0, size+1);
134 
135 	if (len == 0) {
136 		str[0] = '\0';
137 		*offset = off;
138 		return str;
139 	}
140 
141 	switch (typecode) {
142 	case 0:        /* Binary */
143 		strncpy(str, buf2str(&data[off], len), len*2);
144 		break;
145 
146 	case 1:        /* BCD plus */
147 		for (k=0; k<len; k++)
148 			str[k] = bcd_plus[(data[off+k] & 0x0f)];
149 		str[k] = '\0';
150 		break;
151 
152 	case 2:        /* 6-bit ASCII */
153 		for (i=j=0; i<len; i+=3) {
154 			u.bits = 0;
155 			k = ((len-i) < 3 ? (len-i) : 3);
156 #if WORDS_BIGENDIAN
157 			u.chars[3] = data[off+i];
158 			u.chars[2] = (k > 1 ? data[off+i+1] : 0);
159 			u.chars[1] = (k > 2 ? data[off+i+2] : 0);
160 #define CHAR_IDX 3
161 #else
162 			memcpy((void *)&u.bits, &data[off+i], k);
163 #define CHAR_IDX 0
164 #endif
165 			for (k=0; k<4; k++) {
166 				str[j++] = ((u.chars[CHAR_IDX] & 0x3f) + 0x20);
167 				u.bits >>= 6;
168 			}
169 		}
170 		str[j] = '\0';
171 		break;
172 
173 	case 3:
174 		memcpy(str, &data[off], len);
175 		str[len] = '\0';
176 		break;
177 	}
178 
179 	off += len;
180 	*offset = off;
181 
182 	return str;
183 }
184 
185 /* is_valid_filename - checks file/path supplied by user
186  *
187  * input_filename - user input string
188  *
189  * returns   0  if path is ok
190  * returns (-1) if path is NULL
191  * returns (-2) if path is too short
192  * returns (-3) if path is too long
193  */
194 int
195 is_valid_filename(const char *input_filename)
196 {
197 	if (input_filename == NULL) {
198 		lprintf(LOG_ERR, "ERROR: NULL pointer passed.");
199 		return (-1);
200 	}
201 
202 	if (strlen(input_filename) < 1) {
203 		lprintf(LOG_ERR, "File/path is invalid.");
204 		return (-2);
205 	}
206 
207 	if (strlen(input_filename) >= 512) {
208 		lprintf(LOG_ERR, "File/path must be shorter than 512 bytes.");
209 		return (-3);
210 	}
211 
212 	return 0;
213 } /* is_valid_filename() */
214 
215 /* build_fru_bloc  -  build fru bloc for write protection
216 *
217 * @intf:     ipmi interface
218 * @fru_info: information about FRU device
219 * @id      : Fru id
220 * @soffset : Source offset      (from buffer)
221 * @doffset : Destination offset (in device)
222 * @length  : Size of data to write (in bytes)
223 * @pFrubuf : Pointer on data to write
224 *
225 * returns 0 on success
226 * returns -1 on error
227 */
228 #define FRU_NUM_BLOC_COMMON_HEADER  6
229 t_ipmi_fru_bloc *
230 build_fru_bloc(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id)
231 {
232 	t_ipmi_fru_bloc * p_first, * p_bloc, * p_new;
233 	struct ipmi_rs * rsp;
234 	struct ipmi_rq req;
235 	struct fru_header header;
236 	struct fru_multirec_header rec_hdr;
237 	uint8_t msg_data[4];
238 	uint32_t off;
239 	uint16_t i;
240 
241 	/*
242 	* get COMMON Header format
243 	*/
244 	msg_data[0] = id;
245 	msg_data[1] = 0;
246 	msg_data[2] = 0;
247 	msg_data[3] = 8;
248 
249 	memset(&req, 0, sizeof(req));
250 	req.msg.netfn = IPMI_NETFN_STORAGE;
251 	req.msg.cmd = GET_FRU_DATA;
252 	req.msg.data = msg_data;
253 	req.msg.data_len = 4;
254 
255 	rsp = intf->sendrecv(intf, &req);
256 
257 	if (rsp == NULL) {
258 		lprintf(LOG_ERR, " Device not present (No Response)");
259 		return NULL;
260 	}
261 
262 	if (rsp->ccode > 0) {
263 		lprintf(LOG_ERR," Device not present (%s)",
264 				val2str(rsp->ccode, completion_code_vals));
265 		return NULL;
266 	}
267 
268 	if (verbose > 1) {
269 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
270 	}
271 
272 	memcpy(&header, rsp->data + 1, 8);
273 
274 	/* verify header checksum */
275 	if (ipmi_csum((uint8_t *)&header, 8)) {
276 		lprintf(LOG_ERR, " Bad header checksum");
277 		return NULL;
278 	}
279 
280 	if (header.version != 1) {
281 		lprintf(LOG_ERR, " Unknown FRU header version 0x%02x", header.version);
282 		return NULL;
283 	}
284 
285 	/******************************************
286 		Malloc and fill up the bloc contents
287 	*******************************************/
288 
289 	// Common header
290 	p_first = malloc(sizeof(struct ipmi_fru_bloc));
291 	if (!p_first) {
292 		lprintf(LOG_ERR, "ipmitool: malloc failure");
293 		return NULL;
294 	}
295 
296 	p_bloc = p_first;
297 	p_bloc->next = NULL;
298 	p_bloc->start= 0;
299 	p_bloc->size = fru->size;
300 	strcpy((char *)p_bloc->blocId, "Common Header Section");
301 
302 	for (i = 0; i < 4; i++) {
303 		if (header.offsets[i]) {
304 			p_new = malloc(sizeof(struct ipmi_fru_bloc));
305 			if (!p_new) {
306 				lprintf(LOG_ERR, "ipmitool: malloc failure");
307 				free_fru_bloc(p_first);
308 				return NULL;
309 			}
310 
311 			p_new->next = NULL;
312 			p_new->start = header.offsets[i] * 8;
313 			p_new->size = fru->size - p_new->start;
314 
315 			strncpy((char *)p_new->blocId, section_id[i], sizeof(p_new->blocId));
316 			/* Make sure string is null terminated */
317 			p_new->blocId[sizeof(p_new->blocId)-1] = 0;
318 
319 			p_bloc->next = p_new;
320 			p_bloc->size = p_new->start - p_bloc->start;
321 			p_bloc = p_new;
322 		}
323 	}
324 
325 	// Multi
326 	if (header.offset.multi) {
327 		off = header.offset.multi * 8;
328 
329 		do {
330 			/*
331 			 * check for odd offset for the case of fru devices
332 			 * accessed by words
333 			 */
334 			if (fru->access && (off & 1)) {
335 				lprintf(LOG_ERR, " Unaligned offset for a block: %d", off);
336 				/* increment offset */
337 				off++;
338 				break;
339 			}
340 
341 			if (read_fru_area(intf, fru, id, off, 5,
342 					(uint8_t *) &rec_hdr) < 0) {
343 				break;
344 			}
345 
346 			p_new = malloc(sizeof(struct ipmi_fru_bloc));
347 			if (!p_new) {
348 				lprintf(LOG_ERR, "ipmitool: malloc failure");
349 				free_fru_bloc(p_first);
350 				return NULL;
351 			}
352 
353 			p_new->next = NULL;
354 			p_new->start = off;
355 			p_new->size = fru->size - p_new->start;
356 			sprintf((char *)p_new->blocId, "Multi-Rec Area: Type %i",
357 					rec_hdr.type);
358 
359 			p_bloc->next = p_new;
360 			p_bloc->size = p_new->start - p_bloc->start;
361 			p_bloc = p_new;
362 
363 			off += rec_hdr.len + sizeof(struct fru_multirec_header);
364 
365 			/* verify record header */
366 			if (ipmi_csum((uint8_t *)&rec_hdr,
367 					sizeof(struct fru_multirec_header))) {
368 				/* can't reliably judge for the rest space */
369 				break;
370 			}
371 		} while (!(rec_hdr.format & 0x80) && (off < fru->size));
372 
373 		lprintf(LOG_DEBUG,"Multi-Record area ends at: %i (%xh)", off, off);
374 
375 		if (fru->size > off) {
376 			// Bloc for remaining space
377 			p_new = malloc(sizeof(struct ipmi_fru_bloc));
378 			if (!p_new) {
379 				lprintf(LOG_ERR, "ipmitool: malloc failure");
380 				free_fru_bloc(p_first);
381 				return NULL;
382 			}
383 
384 			p_new->next = NULL;
385 			p_new->start = off;
386 			p_new->size = fru->size - p_new->start;
387 			strcpy((char *)p_new->blocId, "Unused space");
388 
389 			p_bloc->next = p_new;
390 			p_bloc->size = p_new->start - p_bloc->start;
391 		}
392 	}
393 
394 	/* Dump blocs */
395 	for(p_bloc = p_first, i = 0; p_bloc; p_bloc = p_bloc->next) {
396 		lprintf(LOG_DEBUG ,"Bloc Numb : %i", i++);
397 		lprintf(LOG_DEBUG ,"Bloc Id   : %s", p_bloc->blocId);
398 		lprintf(LOG_DEBUG ,"Bloc Start: %i", p_bloc->start);
399 		lprintf(LOG_DEBUG ,"Bloc Size : %i", p_bloc->size);
400 		lprintf(LOG_DEBUG ,"");
401 	}
402 
403 	return p_first;
404 }
405 
406 void
407 free_fru_bloc(t_ipmi_fru_bloc *bloc)
408 {
409 	t_ipmi_fru_bloc * del;
410 
411 	while (bloc) {
412 		del = bloc;
413 		bloc = bloc->next;
414 		free(del);
415 		del = NULL;
416 	}
417 }
418 
419 /*
420  * write FRU[doffset:length] from the pFrubuf[soffset:length]
421  * rc=1 on success
422 **/
423 int
424 write_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
425 					uint16_t soffset,  uint16_t doffset,
426 					uint16_t length, uint8_t *pFrubuf)
427 {
428 	uint16_t tmp, finish;
429 	struct ipmi_rs * rsp;
430 	struct ipmi_rq req;
431 	uint8_t msg_data[255+3];
432 	uint16_t writeLength;
433 	uint16_t found_bloc = 0;
434 
435 	finish = doffset + length;        /* destination offset */
436 	if (finish > fru->size)
437 	{
438 		lprintf(LOG_ERROR, "Return error");
439 		return -1;
440 	}
441 
442 	if (fru->access && ((doffset & 1) || (length & 1))) {
443 		lprintf(LOG_ERROR, "Odd offset or length specified");
444 		return (-1);
445 	}
446 
447 	t_ipmi_fru_bloc * fru_bloc = build_fru_bloc(intf, fru, id);
448 	t_ipmi_fru_bloc * saved_fru_bloc = fru_bloc;
449 
450 	memset(&req, 0, sizeof(req));
451 	req.msg.netfn = IPMI_NETFN_STORAGE;
452 	req.msg.cmd = SET_FRU_DATA;
453 	req.msg.data = msg_data;
454 
455 	/* initialize request size only once */
456 	if (fru->max_write_size == 0) {
457 		uint16_t max_rq_size = ipmi_intf_get_max_request_data_size(intf);
458 
459 		/* validate lower bound of the maximum request data size */
460 		if (max_rq_size <= 3) {
461 			lprintf(LOG_ERROR, "Maximum request size is too small to send "
462 					"a write request");
463 			return -1;
464 		}
465 
466 		/*
467 		 * Write FRU Info command returns the number of written bytes in
468 		 * a single byte field.
469 		 */
470 		if (max_rq_size - 3 > 255) {
471 			/*  Limit the max write size with 255 bytes. */
472 			fru->max_write_size = 255;
473 		} else {
474 			/* subtract 1 byte for FRU ID an 2 bytes for offset */
475 			fru->max_write_size = max_rq_size - 3;
476 		}
477 
478 		/* check word access */
479 		if (fru->access) {
480 			fru->max_write_size &= ~1;
481 		}
482 	}
483 
484 	do {
485 		uint16_t end_bloc;
486 		uint8_t protected_bloc = 0;
487 
488 		/* Write per bloc, try to find the end of a bloc*/
489 		while (fru_bloc && fru_bloc->start + fru_bloc->size <= doffset) {
490 			fru_bloc = fru_bloc->next;
491 			found_bloc++;
492 		}
493 
494 		if (fru_bloc && fru_bloc->start + fru_bloc->size < finish) {
495 			end_bloc = fru_bloc->start + fru_bloc->size;
496 		} else {
497 			end_bloc = finish;
498 		}
499 
500 		/* calculate write length */
501 		tmp = end_bloc - doffset;
502 
503 		/* check that write length is more than maximum request size */
504 		if (tmp > fru->max_write_size) {
505 			writeLength = fru->max_write_size;
506 		} else {
507 			writeLength = tmp;
508 		}
509 
510 		/* copy fru data */
511 		memcpy(&msg_data[3], pFrubuf + soffset, writeLength);
512 
513 		/* check word access */
514 		if (fru->access) {
515 			writeLength &= ~1;
516 		}
517 
518 		tmp = doffset;
519 		if (fru->access) {
520 			tmp >>= 1;
521 		}
522 
523 		msg_data[0] = id;
524 		msg_data[1] = (uint8_t)tmp;
525 		msg_data[2] = (uint8_t)(tmp >> 8);
526 		req.msg.data_len = writeLength + 3;
527 
528 		if(fru_bloc) {
529 			lprintf(LOG_INFO,"Writing %d bytes (Bloc #%i: %s)",
530 					writeLength, found_bloc, fru_bloc->blocId);
531 		} else {
532 			lprintf(LOG_INFO,"Writing %d bytes", writeLength);
533 		}
534 
535 		rsp = intf->sendrecv(intf, &req);
536 		if (!rsp) {
537 			break;
538 		}
539 
540 		if (rsp->ccode == 0xc7 || rsp->ccode == 0xc8 || rsp->ccode == 0xca) {
541 			if (fru->max_write_size > 8) {
542 				fru->max_write_size -= 8;
543 				lprintf(LOG_INFO, "Retrying FRU write with request size %d",
544 						fru->max_write_size);
545 				continue;
546 			}
547 		} else if(rsp->ccode == 0x80) {
548 			rsp->ccode = 0;
549 			// Write protected section
550 			protected_bloc = 1;
551 		}
552 
553 		if (rsp->ccode > 0)
554 			break;
555 
556 		if (protected_bloc == 0) {
557 			// Write OK, bloc not protected, continue
558 			lprintf(LOG_INFO,"Wrote %d bytes", writeLength);
559 			doffset += writeLength;
560 			soffset += writeLength;
561 		} else {
562 			if(fru_bloc) {
563 				// Bloc protected, advise user and jump over protected bloc
564 				lprintf(LOG_INFO,
565 						"Bloc [%s] protected at offset: %i (size %i bytes)",
566 						fru_bloc->blocId, fru_bloc->start, fru_bloc->size);
567 				lprintf(LOG_INFO,"Jumping over this bloc");
568 			} else {
569 				lprintf(LOG_INFO,
570 						"Remaining FRU is protected following offset: %i",
571 						doffset);
572 			}
573 			soffset += end_bloc - doffset;
574 			doffset = end_bloc;
575 		}
576 	} while (doffset < finish);
577 
578 	if (saved_fru_bloc) {
579 		free_fru_bloc(saved_fru_bloc);
580 	}
581 
582 	return doffset >= finish;
583 }
584 
585 /* read_fru_area  -  fill in frubuf[offset:length] from the FRU[offset:length]
586 *
587 * @intf:   ipmi interface
588 * @fru: fru info
589 * @id:     fru id
590 * @offset: offset into buffer
591 * @length: how much to read
592 * @frubuf: buffer read into
593 *
594 * returns -1 on error
595 * returns 0 if successful
596 */
597 int
598 read_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
599 			uint32_t offset, uint32_t length, uint8_t *frubuf)
600 {
601 	uint32_t off = offset, tmp, finish;
602 	struct ipmi_rs * rsp;
603 	struct ipmi_rq req;
604 	uint8_t msg_data[4];
605 
606 	if (offset > fru->size) {
607 		lprintf(LOG_ERR, "Read FRU Area offset incorrect: %d > %d",
608 			offset, fru->size);
609 		return -1;
610 	}
611 
612 	finish = offset + length;
613 	if (finish > fru->size) {
614 		finish = fru->size;
615 		lprintf(LOG_NOTICE, "Read FRU Area length %d too large, "
616 			"Adjusting to %d",
617 			offset + length, finish - offset);
618 	}
619 
620 	memset(&req, 0, sizeof(req));
621 	req.msg.netfn = IPMI_NETFN_STORAGE;
622 	req.msg.cmd = GET_FRU_DATA;
623 	req.msg.data = msg_data;
624 	req.msg.data_len = 4;
625 
626 	if (fru->max_read_size == 0) {
627 		uint16_t max_rs_size = ipmi_intf_get_max_response_data_size(intf) - 1;
628 
629 		/* validate lower bound of the maximum response data size */
630 		if (max_rs_size <= 1) {
631 			lprintf(LOG_ERROR, "Maximum response size is too small to send "
632 					"a read request");
633 			return -1;
634 		}
635 
636 		/*
637 		 * Read FRU Info command may read up to 255 bytes of data.
638 		 */
639 		if (max_rs_size - 1 > 255) {
640 			/*  Limit the max read size with 255 bytes. */
641 			fru->max_read_size = 255;
642 		} else {
643 			/* subtract 1 byte for bytes count */
644 			fru->max_read_size = max_rs_size - 1;
645 		}
646 
647 		/* check word access */
648 		if (fru->access) {
649 			fru->max_read_size &= ~1;
650 		}
651 	}
652 
653 	do {
654 		tmp = fru->access ? off >> 1 : off;
655 		msg_data[0] = id;
656 		msg_data[1] = (uint8_t)(tmp & 0xff);
657 		msg_data[2] = (uint8_t)(tmp >> 8);
658 		tmp = finish - off;
659 		if (tmp > fru->max_read_size)
660 			msg_data[3] = (uint8_t)fru->max_read_size;
661 		else
662 			msg_data[3] = (uint8_t)tmp;
663 
664 		rsp = intf->sendrecv(intf, &req);
665 		if (rsp == NULL) {
666 			lprintf(LOG_NOTICE, "FRU Read failed");
667 			break;
668 		}
669 		if (rsp->ccode > 0) {
670 			/* if we get C8h or CAh completion code then we requested too
671 			* many bytes at once so try again with smaller size */
672 			if ((rsp->ccode == 0xc8 || rsp->ccode == 0xca)
673 					&& fru->max_read_size > 8) {
674 				if (fru->max_read_size > 32) {
675 					/* subtract read length more aggressively */
676 					fru->max_read_size -= 8;
677 				} else {
678 					/* subtract length less aggressively */
679 					fru->max_read_size--;
680 				}
681 
682 				lprintf(LOG_INFO, "Retrying FRU read with request size %d",
683 						fru->max_read_size);
684 				continue;
685 			}
686 
687 			lprintf(LOG_NOTICE, "FRU Read failed: %s",
688 				val2str(rsp->ccode, completion_code_vals));
689 			break;
690 		}
691 
692 		tmp = fru->access ? rsp->data[0] << 1 : rsp->data[0];
693 		memcpy(frubuf, rsp->data + 1, tmp);
694 		off += tmp;
695 		frubuf += tmp;
696 		/* sometimes the size returned in the Info command
697 		* is too large.  return 0 so higher level function
698 		* still attempts to parse what was returned */
699 		if (tmp == 0 && off < finish) {
700 			return 0;
701 		}
702 	} while (off < finish);
703 
704 	if (off < finish) {
705 		return -1;
706 	}
707 
708 	return 0;
709 }
710 
711 /* read_fru_area  -  fill in frubuf[offset:length] from the FRU[offset:length]
712 *
713 * @intf:   ipmi interface
714 * @fru: fru info
715 * @id:     fru id
716 * @offset: offset into buffer
717 * @length: how much to read
718 * @frubuf: buffer read into
719 *
720 * returns -1 on error
721 * returns 0 if successful
722 */
723 int
724 read_fru_area_section(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id,
725 			uint32_t offset, uint32_t length, uint8_t *frubuf)
726 {
727 	static uint32_t fru_data_rqst_size = 20;
728 	uint32_t off = offset, tmp, finish;
729 	struct ipmi_rs * rsp;
730 	struct ipmi_rq req;
731 	uint8_t msg_data[4];
732 
733 	if (offset > fru->size) {
734 		lprintf(LOG_ERR, "Read FRU Area offset incorrect: %d > %d",
735 			offset, fru->size);
736 		return -1;
737 	}
738 
739 	finish = offset + length;
740 	if (finish > fru->size) {
741 		finish = fru->size;
742 		lprintf(LOG_NOTICE, "Read FRU Area length %d too large, "
743 			"Adjusting to %d",
744 			offset + length, finish - offset);
745 	}
746 
747 	memset(&req, 0, sizeof(req));
748 	req.msg.netfn = IPMI_NETFN_STORAGE;
749 	req.msg.cmd = GET_FRU_DATA;
750 	req.msg.data = msg_data;
751 	req.msg.data_len = 4;
752 
753 #ifdef LIMIT_ALL_REQUEST_SIZE
754 	if (fru_data_rqst_size > 16)
755 #else
756 	if (fru->access && fru_data_rqst_size > 16)
757 #endif
758 		fru_data_rqst_size = 16;
759 	do {
760 		tmp = fru->access ? off >> 1 : off;
761 		msg_data[0] = id;
762 		msg_data[1] = (uint8_t)(tmp & 0xff);
763 		msg_data[2] = (uint8_t)(tmp >> 8);
764 		tmp = finish - off;
765 		if (tmp > fru_data_rqst_size)
766 			msg_data[3] = (uint8_t)fru_data_rqst_size;
767 		else
768 			msg_data[3] = (uint8_t)tmp;
769 
770 		rsp = intf->sendrecv(intf, &req);
771 		if (rsp == NULL) {
772 			lprintf(LOG_NOTICE, "FRU Read failed");
773 			break;
774 		}
775 		if (rsp->ccode > 0) {
776 			/* if we get C7 or C8  or CA return code then we requested too
777 			* many bytes at once so try again with smaller size */
778 			if ((rsp->ccode == 0xc7 || rsp->ccode == 0xc8 || rsp->ccode == 0xca) &&
779 				(--fru_data_rqst_size > 8)) {
780 				lprintf(LOG_INFO, "Retrying FRU read with request size %d",
781 					fru_data_rqst_size);
782 				continue;
783 			}
784 			lprintf(LOG_NOTICE, "FRU Read failed: %s",
785 				val2str(rsp->ccode, completion_code_vals));
786 			break;
787 		}
788 
789 		tmp = fru->access ? rsp->data[0] << 1 : rsp->data[0];
790 		memcpy((frubuf + off)-offset, rsp->data + 1, tmp);
791 		off += tmp;
792 
793 		/* sometimes the size returned in the Info command
794 		* is too large.  return 0 so higher level function
795 		* still attempts to parse what was returned */
796 		if (tmp == 0 && off < finish)
797 			return 0;
798 
799 	} while (off < finish);
800 
801 	if (off < finish)
802 		return -1;
803 
804 	return 0;
805 }
806 
807 
808 static void
809 fru_area_print_multirec_bloc(struct ipmi_intf * intf, struct fru_info * fru,
810 			uint8_t id, uint32_t offset)
811 {
812 	uint8_t * fru_data = NULL;
813 	uint32_t fru_len, i;
814 	struct fru_multirec_header * h;
815 	uint32_t last_off, len;
816 
817 	i = last_off = offset;
818 	fru_len = 0;
819 
820 	fru_data = malloc(fru->size + 1);
821 	if (fru_data == NULL) {
822 		lprintf(LOG_ERR, " Out of memory!");
823 		return;
824 	}
825 
826 	memset(fru_data, 0, fru->size + 1);
827 
828 	do {
829 		h = (struct fru_multirec_header *) (fru_data + i);
830 
831 		// read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time
832 		if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len)))
833 		{
834 			len = fru->size - last_off;
835 			if (len > FRU_MULTIREC_CHUNK_SIZE)
836 				len = FRU_MULTIREC_CHUNK_SIZE;
837 
838 			if (read_fru_area(intf, fru, id, last_off, len, fru_data) < 0)
839 				break;
840 
841 			last_off += len;
842 		}
843 
844 		//printf("Bloc Numb : %i\n", counter);
845 		printf("Bloc Start: %i\n", i);
846 		printf("Bloc Size : %i\n", h->len);
847 		printf("\n");
848 
849 		i += h->len + sizeof (struct fru_multirec_header);
850 	} while (!(h->format & 0x80));
851 
852 	i = offset;
853 	do {
854 		h = (struct fru_multirec_header *) (fru_data + i);
855 
856 		printf("Bloc Start: %i\n", i);
857 		printf("Bloc Size : %i\n", h->len);
858 		printf("\n");
859 
860 		i += h->len + sizeof (struct fru_multirec_header);
861 	} while (!(h->format & 0x80));
862 
863 	lprintf(LOG_DEBUG ,"Multi-Record area ends at: %i (%xh)",i,i);
864 
865 	free(fru_data);
866 	fru_data = NULL;
867 }
868 
869 
870 /* fru_area_print_chassis  -  Print FRU Chassis Area
871 *
872 * @intf:   ipmi interface
873 * @fru: fru info
874 * @id:  fru id
875 * @offset: offset pointer
876 */
877 static void
878 fru_area_print_chassis(struct ipmi_intf * intf, struct fru_info * fru,
879 			uint8_t id, uint32_t offset)
880 {
881 	char * fru_area;
882 	uint8_t * fru_data;
883 	uint32_t fru_len, i;
884 	uint8_t tmp[2];
885 
886 	fru_len = 0;
887 
888 	/* read enough to check length field */
889 	if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) {
890 		fru_len = 8 * tmp[1];
891 	}
892 
893 	if (fru_len == 0) {
894 		return;
895 	}
896 
897 	fru_data = malloc(fru_len);
898 	if (fru_data == NULL) {
899 		lprintf(LOG_ERR, "ipmitool: malloc failure");
900 		return;
901 	}
902 
903 	memset(fru_data, 0, fru_len);
904 
905 	/* read in the full fru */
906 	if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) {
907 		free(fru_data);
908 		fru_data = NULL;
909 		return;
910 	}
911 
912 	/*
913 	 * skip first two bytes which specify
914 	 * fru area version and fru area length
915 	 */
916 	i = 2;
917 
918 	printf(" Chassis Type          : %s\n",
919  		chassis_type_desc[fru_data[i] >
920  		(sizeof(chassis_type_desc)/sizeof(chassis_type_desc[0])) - 1 ?
921  		2 : fru_data[i]]);
922 
923  	i++;
924 
925 	fru_area = get_fru_area_str(fru_data, &i);
926 	if (fru_area != NULL) {
927 		if (strlen(fru_area) > 0) {
928 			printf(" Chassis Part Number   : %s\n", fru_area);
929 		}
930 		free(fru_area);
931 		fru_area = NULL;
932 	}
933 
934 	fru_area = get_fru_area_str(fru_data, &i);
935 	if (fru_area != NULL) {
936 		if (strlen(fru_area) > 0) {
937 			printf(" Chassis Serial        : %s\n", fru_area);
938 		}
939 		free(fru_area);
940 		fru_area = NULL;
941 	}
942 
943 	/* read any extra fields */
944 	while ((fru_data[i] != 0xc1) && (i < fru_len))
945 	{
946 		int j = i;
947 		fru_area = get_fru_area_str(fru_data, &i);
948 		if (fru_area != NULL) {
949 			if (strlen(fru_area) > 0) {
950 				printf(" Chassis Extra         : %s\n", fru_area);
951 			}
952 			free(fru_area);
953 			fru_area = NULL;
954 		}
955 
956 		if (i == j) {
957 			break;
958 		}
959 	}
960 
961 	if (fru_area != NULL) {
962 		free(fru_data);
963 		fru_data = NULL;
964 	}
965 }
966 
967 /* fru_area_print_board  -  Print FRU Board Area
968 *
969 * @intf:   ipmi interface
970 * @fru: fru info
971 * @id:  fru id
972 * @offset: offset pointer
973 */
974 static void
975 fru_area_print_board(struct ipmi_intf * intf, struct fru_info * fru,
976 			uint8_t id, uint32_t offset)
977 {
978 	char * fru_area;
979 	uint8_t * fru_data;
980 	uint32_t fru_len;
981 	uint32_t i;
982 	time_t tval;
983 	uint8_t tmp[2];
984 
985 	fru_len = 0;
986 
987 	/* read enough to check length field */
988 	if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) {
989 		fru_len = 8 * tmp[1];
990 	}
991 
992 	if (fru_len <= 0) {
993 		return;
994 	}
995 
996 	fru_data = malloc(fru_len);
997 	if (fru_data == NULL) {
998 		lprintf(LOG_ERR, "ipmitool: malloc failure");
999 		return;
1000 	}
1001 
1002 	memset(fru_data, 0, fru_len);
1003 
1004 	/* read in the full fru */
1005 	if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) {
1006 		free(fru_data);
1007 		fru_data = NULL;
1008 		return;
1009 	}
1010 
1011 	/*
1012 	 * skip first three bytes which specify
1013 	 * fru area version, fru area length
1014 	 * and fru board language
1015 	 */
1016 	i = 3;
1017 
1018 	tval=((fru_data[i+2] << 16) + (fru_data[i+1] << 8) + (fru_data[i]));
1019 	tval=tval * 60;
1020 	tval=tval + secs_from_1970_1996;
1021 	printf(" Board Mfg Date        : %s", asctime(localtime(&tval)));
1022 	i += 3;  /* skip mfg. date time */
1023 
1024 	fru_area = get_fru_area_str(fru_data, &i);
1025 	if (fru_area != NULL) {
1026 		if (strlen(fru_area) > 0) {
1027 			printf(" Board Mfg             : %s\n", fru_area);
1028 		}
1029 		free(fru_area);
1030 		fru_area = NULL;
1031 	}
1032 
1033 	fru_area = get_fru_area_str(fru_data, &i);
1034 	if (fru_area != NULL) {
1035 		if (strlen(fru_area) > 0) {
1036 			printf(" Board Product         : %s\n", fru_area);
1037 		}
1038 		free(fru_area);
1039 		fru_area = NULL;
1040 	}
1041 
1042 	fru_area = get_fru_area_str(fru_data, &i);
1043 	if (fru_area != NULL) {
1044 		if (strlen(fru_area) > 0) {
1045 			printf(" Board Serial          : %s\n", fru_area);
1046 		}
1047 		free(fru_area);
1048 		fru_area = NULL;
1049 	}
1050 
1051 	fru_area = get_fru_area_str(fru_data, &i);
1052 	if (fru_area != NULL) {
1053 		if (strlen(fru_area) > 0) {
1054 			printf(" Board Part Number     : %s\n", fru_area);
1055 		}
1056 		free(fru_area);
1057 		fru_area = NULL;
1058 	}
1059 
1060 	fru_area = get_fru_area_str(fru_data, &i);
1061 	if (fru_area != NULL) {
1062 		if (strlen(fru_area) > 0 && verbose > 0) {
1063 			printf(" Board FRU ID          : %s\n", fru_area);
1064 		}
1065 		free(fru_area);
1066 		fru_area = NULL;
1067 	}
1068 
1069 	/* read any extra fields */
1070 	while ((fru_data[i] != 0xc1) && (i < fru_len))
1071 	{
1072 		int j = i;
1073 		fru_area = get_fru_area_str(fru_data, &i);
1074 		if (fru_area != NULL) {
1075 			if (strlen(fru_area) > 0) {
1076 				printf(" Board Extra           : %s\n", fru_area);
1077 			}
1078 			free(fru_area);
1079 			fru_area = NULL;
1080 		}
1081 		if (i == j)
1082 			break;
1083 	}
1084 
1085 	if (fru_area != NULL) {
1086 		free(fru_data);
1087 		fru_data = NULL;
1088 	}
1089 }
1090 
1091 /* fru_area_print_product  -  Print FRU Product Area
1092 *
1093 * @intf:   ipmi interface
1094 * @fru: fru info
1095 * @id:  fru id
1096 * @offset: offset pointer
1097 */
1098 static void
1099 fru_area_print_product(struct ipmi_intf * intf, struct fru_info * fru,
1100 				uint8_t id, uint32_t offset)
1101 {
1102 	char * fru_area;
1103 	uint8_t * fru_data;
1104 	uint32_t fru_len, i;
1105 	uint8_t tmp[2];
1106 
1107 	fru_len = 0;
1108 
1109 	/* read enough to check length field */
1110 	if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) {
1111 		fru_len = 8 * tmp[1];
1112 	}
1113 
1114 	if (fru_len == 0) {
1115 		return;
1116 	}
1117 
1118 	fru_data = malloc(fru_len);
1119 	if (fru_data == NULL) {
1120 		lprintf(LOG_ERR, "ipmitool: malloc failure");
1121 		return;
1122 	}
1123 
1124 	memset(fru_data, 0, fru_len);
1125 
1126 
1127 	/* read in the full fru */
1128 	if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) {
1129 		free(fru_data);
1130 		fru_data = NULL;
1131 		return;
1132 	}
1133 
1134 	/*
1135 	 * skip first three bytes which specify
1136 	 * fru area version, fru area length
1137 	 * and fru board language
1138 	 */
1139 	i = 3;
1140 
1141 	fru_area = get_fru_area_str(fru_data, &i);
1142 	if (fru_area != NULL) {
1143 		if (strlen(fru_area) > 0) {
1144 			printf(" Product Manufacturer  : %s\n", fru_area);
1145 		}
1146 		free(fru_area);
1147 		fru_area = NULL;
1148 	}
1149 
1150 	fru_area = get_fru_area_str(fru_data, &i);
1151 	if (fru_area != NULL) {
1152 		if (strlen(fru_area) > 0) {
1153 			printf(" Product Name          : %s\n", fru_area);
1154 		}
1155 		free(fru_area);
1156 		fru_area = NULL;
1157 	}
1158 
1159 	fru_area = get_fru_area_str(fru_data, &i);
1160 	if (fru_area != NULL) {
1161 		if (strlen(fru_area) > 0) {
1162 			printf(" Product Part Number   : %s\n", fru_area);
1163 		}
1164 		free(fru_area);
1165 		fru_area = NULL;
1166 	}
1167 
1168 	fru_area = get_fru_area_str(fru_data, &i);
1169 	if (fru_area != NULL) {
1170 		if (strlen(fru_area) > 0) {
1171 			printf(" Product Version       : %s\n", fru_area);
1172 		}
1173 		free(fru_area);
1174 		fru_area = NULL;
1175 	}
1176 
1177 	fru_area = get_fru_area_str(fru_data, &i);
1178 	if (fru_area != NULL) {
1179 		if (strlen(fru_area) > 0) {
1180 			printf(" Product Serial        : %s\n", fru_area);
1181 		}
1182 		free(fru_area);
1183 		fru_area = NULL;
1184 	}
1185 
1186 	fru_area = get_fru_area_str(fru_data, &i);
1187 	if (fru_area != NULL) {
1188 		if (strlen(fru_area) > 0) {
1189 			printf(" Product Asset Tag     : %s\n", fru_area);
1190 		}
1191 		free(fru_area);
1192 		fru_area = NULL;
1193 	}
1194 
1195 	fru_area = get_fru_area_str(fru_data, &i);
1196 	if (fru_area != NULL) {
1197 		if (strlen(fru_area) > 0 && verbose > 0) {
1198 			printf(" Product FRU ID        : %s\n", fru_area);
1199 		}
1200 		free(fru_area);
1201 		fru_area = NULL;
1202 	}
1203 
1204 	/* read any extra fields */
1205 	while ((fru_data[i] != 0xc1) && (i < fru_len))
1206 	{
1207 		int j = i;
1208 		fru_area = get_fru_area_str(fru_data, &i);
1209 		if (fru_area != NULL) {
1210 			if (strlen(fru_area) > 0) {
1211 				printf(" Product Extra         : %s\n", fru_area);
1212 			}
1213 			free(fru_area);
1214 			fru_area = NULL;
1215 		}
1216 		if (i == j)
1217 			break;
1218 	}
1219 
1220 	if (fru_area != NULL) {
1221 		free(fru_data);
1222 		fru_data = NULL;
1223 	}
1224 }
1225 
1226 /* fru_area_print_multirec  -  Print FRU Multi Record Area
1227 *
1228 * @intf:   ipmi interface
1229 * @fru: fru info
1230 * @id:  fru id
1231 * @offset: offset pointer
1232 */
1233 static void
1234 fru_area_print_multirec(struct ipmi_intf * intf, struct fru_info * fru,
1235 			uint8_t id, uint32_t offset)
1236 {
1237 	uint8_t * fru_data;
1238 	struct fru_multirec_header * h;
1239 	struct fru_multirec_powersupply * ps;
1240 	struct fru_multirec_dcoutput * dc;
1241 	struct fru_multirec_dcload * dl;
1242 	uint16_t peak_capacity;
1243 	uint8_t peak_hold_up_time;
1244 	uint32_t last_off;
1245 
1246 	last_off = offset;
1247 
1248 	fru_data = malloc(FRU_MULTIREC_CHUNK_SIZE);
1249 	if (fru_data == NULL) {
1250 		lprintf(LOG_ERR, "ipmitool: malloc failure");
1251 		return;
1252 	}
1253 
1254 	memset(fru_data, 0, FRU_MULTIREC_CHUNK_SIZE);
1255 
1256 	h = (struct fru_multirec_header *) (fru_data);
1257 
1258 	do {
1259 		if (read_fru_area(intf, fru, id, last_off, sizeof(*h), fru_data) < 0) {
1260 			break;
1261 		}
1262 
1263 		if (h->len && read_fru_area(intf, fru, id,
1264 				last_off + sizeof(*h), h->len, fru_data + sizeof(*h)) < 0) {
1265 			break;
1266 		}
1267 
1268 		last_off += h->len + sizeof(*h);
1269 
1270 		switch (h->type) {
1271 		case FRU_RECORD_TYPE_POWER_SUPPLY_INFORMATION:
1272 			ps = (struct fru_multirec_powersupply *)
1273 				(fru_data + sizeof(struct fru_multirec_header));
1274 
1275 #if WORDS_BIGENDIAN
1276 			ps->capacity      = BSWAP_16(ps->capacity);
1277 			ps->peak_va    = BSWAP_16(ps->peak_va);
1278 			ps->lowend_input1 = BSWAP_16(ps->lowend_input1);
1279 			ps->highend_input1   = BSWAP_16(ps->highend_input1);
1280 			ps->lowend_input2 = BSWAP_16(ps->lowend_input2);
1281 			ps->highend_input2   = BSWAP_16(ps->highend_input2);
1282 			ps->combined_capacity   = BSWAP_16(ps->combined_capacity);
1283 			ps->peak_cap_ht      = BSWAP_16(ps->peak_cap_ht);
1284 #endif
1285 			peak_hold_up_time = (ps->peak_cap_ht & 0xf000) >> 12;
1286 			peak_capacity     = ps->peak_cap_ht & 0x0fff;
1287 
1288 			printf (" Power Supply Record\n");
1289 			printf ("  Capacity                   : %d W\n",
1290 				ps->capacity);
1291 			printf ("  Peak VA                    : %d VA\n",
1292 				ps->peak_va);
1293 			printf ("  Inrush Current             : %d A\n",
1294 				ps->inrush_current);
1295 			printf ("  Inrush Interval            : %d ms\n",
1296 				ps->inrush_interval);
1297 			printf ("  Input Voltage Range 1      : %d-%d V\n",
1298 				ps->lowend_input1 / 100, ps->highend_input1 / 100);
1299 			printf ("  Input Voltage Range 2      : %d-%d V\n",
1300 				ps->lowend_input2 / 100, ps->highend_input2 / 100);
1301 			printf ("  Input Frequency Range      : %d-%d Hz\n",
1302 				ps->lowend_freq, ps->highend_freq);
1303 			printf ("  A/C Dropout Tolerance      : %d ms\n",
1304 				ps->dropout_tolerance);
1305 			printf ("  Flags                      : %s%s%s%s%s\n",
1306 				ps->predictive_fail ? "'Predictive fail' " : "",
1307 				ps->pfc ? "'Power factor correction' " : "",
1308 				ps->autoswitch ? "'Autoswitch voltage' " : "",
1309 				ps->hotswap ? "'Hot swap' " : "",
1310 				ps->predictive_fail ? ps->rps_threshold ?
1311 				ps->tach ? "'Two pulses per rotation'" : "'One pulse per rotation'" :
1312 				ps->tach ? "'Failure on pin de-assertion'" : "'Failure on pin assertion'" : "");
1313 			printf ("  Peak capacity              : %d W\n",
1314 				peak_capacity);
1315 			printf ("  Peak capacity holdup       : %d s\n",
1316 				peak_hold_up_time);
1317 			if (ps->combined_capacity == 0)
1318 				printf ("  Combined capacity          : not specified\n");
1319 			else
1320 				printf ("  Combined capacity          : %d W (%s and %s)\n",
1321 					ps->combined_capacity,
1322 					combined_voltage_desc [ps->combined_voltage1],
1323 					combined_voltage_desc [ps->combined_voltage2]);
1324 			if (ps->predictive_fail)
1325 				printf ("  Fan lower threshold        : %d RPS\n",
1326 					ps->rps_threshold);
1327 			break;
1328 
1329 		case FRU_RECORD_TYPE_DC_OUTPUT:
1330 			dc = (struct fru_multirec_dcoutput *)
1331 				(fru_data + sizeof(struct fru_multirec_header));
1332 
1333 #if WORDS_BIGENDIAN
1334 			dc->nominal_voltage  = BSWAP_16(dc->nominal_voltage);
1335 			dc->max_neg_dev      = BSWAP_16(dc->max_neg_dev);
1336 			dc->max_pos_dev      = BSWAP_16(dc->max_pos_dev);
1337 			dc->ripple_and_noise = BSWAP_16(dc->ripple_and_noise);
1338 			dc->min_current      = BSWAP_16(dc->min_current);
1339 			dc->max_current      = BSWAP_16(dc->max_current);
1340 #endif
1341 
1342 			printf (" DC Output Record\n");
1343 			printf ("  Output Number              : %d\n",
1344 				dc->output_number);
1345 			printf ("  Standby power              : %s\n",
1346 				dc->standby ? "Yes" : "No");
1347 			printf ("  Nominal voltage            : %.2f V\n",
1348 				(double) dc->nominal_voltage / 100);
1349 			printf ("  Max negative deviation     : %.2f V\n",
1350 				(double) dc->max_neg_dev / 100);
1351 			printf ("  Max positive deviation     : %.2f V\n",
1352 				(double) dc->max_pos_dev / 100);
1353 			printf ("  Ripple and noise pk-pk     : %d mV\n",
1354 				dc->ripple_and_noise);
1355 			printf ("  Minimum current draw       : %.3f A\n",
1356 				(double) dc->min_current / 1000);
1357 			printf ("  Maximum current draw       : %.3f A\n",
1358 				(double) dc->max_current / 1000);
1359 			break;
1360 
1361 		case FRU_RECORD_TYPE_DC_LOAD:
1362 			dl = (struct fru_multirec_dcload *)
1363 				(fru_data + sizeof(struct fru_multirec_header));
1364 
1365 #if WORDS_BIGENDIAN
1366 			dl->nominal_voltage  = BSWAP_16(dl->nominal_voltage);
1367 			dl->min_voltage      = BSWAP_16(dl->min_voltage);
1368 			dl->max_voltage      = BSWAP_16(dl->max_voltage);
1369 			dl->ripple_and_noise = BSWAP_16(dl->ripple_and_noise);
1370 			dl->min_current      = BSWAP_16(dl->min_current);
1371 			dl->max_current      = BSWAP_16(dl->max_current);
1372 #endif
1373 
1374 			printf (" DC Load Record\n");
1375 			printf ("  Output Number              : %d\n",
1376 				dl->output_number);
1377 			printf ("  Nominal voltage            : %.2f V\n",
1378 				(double) dl->nominal_voltage / 100);
1379 			printf ("  Min voltage allowed        : %.2f V\n",
1380 				(double) dl->min_voltage / 100);
1381 			printf ("  Max voltage allowed        : %.2f V\n",
1382 				(double) dl->max_voltage / 100);
1383 			printf ("  Ripple and noise pk-pk     : %d mV\n",
1384 				dl->ripple_and_noise);
1385 			printf ("  Minimum current load       : %.3f A\n",
1386 				(double) dl->min_current / 1000);
1387 			printf ("  Maximum current load       : %.3f A\n",
1388 				(double) dl->max_current / 1000);
1389 			break;
1390 		case FRU_RECORD_TYPE_OEM_EXTENSION:
1391 			{
1392 				struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *)
1393 										&fru_data[sizeof(struct fru_multirec_header)];
1394 				uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16;
1395 
1396 				/* Now makes sure this is really PICMG record */
1397 
1398 				if( iana == IPMI_OEM_PICMG ){
1399 					printf("  PICMG Extension Record\n");
1400 					ipmi_fru_picmg_ext_print(fru_data,
1401 													sizeof(struct fru_multirec_header),
1402 													h->len);
1403 				}
1404 				/* FIXME: Add OEM record support here */
1405 				else{
1406 					printf("  OEM (%s) Record\n", val2str( iana, ipmi_oem_info));
1407 				}
1408 			}
1409 			break;
1410 		}
1411 	} while (!(h->format & 0x80));
1412 
1413 	lprintf(LOG_DEBUG ,"Multi-Record area ends at: %i (%xh)", last_off, last_off);
1414 
1415 	free(fru_data);
1416 }
1417 
1418 /* ipmi_fru_query_new_value  -  Query new values to replace original FRU content
1419 *
1420 * @data:   FRU data
1421 * @offset: offset of the bytes to be modified in data
1422 * @len:    size of the modified data
1423 *
1424 * returns : TRUE if data changed
1425 * returns : FALSE if data not changed
1426 */
1427 int ipmi_fru_query_new_value(uint8_t *data,int offset, size_t len)
1428 {
1429 	int status=FALSE;
1430 	int ret;
1431 	char answer;
1432 
1433 	printf("Would you like to change this value <y/n> ? ");
1434 	ret = scanf("%c", &answer);
1435 	if (ret != 1) {
1436 		return FALSE;
1437 	}
1438 
1439 	if( answer == 'y' || answer == 'Y' ){
1440 		int i;
1441 		unsigned int *holder;
1442 
1443 		holder = malloc(len);
1444 		printf(
1445 		 "Enter hex values for each of the %d entries (lsb first), "
1446 		 "hit <enter> between entries\n", (int)len);
1447 
1448 		/* I can't assign scanf' %x into a single char */
1449 		for( i=0;i<len;i++ ){
1450 			ret = scanf("%x", holder+i);
1451 			if (ret != 1) {
1452 				free(holder);
1453 				return FALSE;
1454 			}
1455 		}
1456 		for( i=0;i<len;i++ ){
1457 			data[offset++] = (unsigned char) *(holder+i);
1458 		}
1459 		/* &data[offset++] */
1460 		free(holder);
1461 		holder = NULL;
1462 		status = TRUE;
1463 	}
1464 	else{
1465 		printf("Entered %c\n",answer);
1466 	}
1467 
1468 	return status;
1469 }
1470 
1471 /* ipmi_fru_oemkontron_edit  -
1472 *  Query new values to replace original FRU content
1473 *  This is a generic enough to support any type of 'OEM' record
1474 *  because the user supplies 'IANA number' , 'record Id' and 'record' version'
1475 *
1476 * However, the parser must have 'apriori' knowledge of the record format
1477 * The currently supported record is :
1478 *
1479 *    IANA          : 15000  (Kontron)
1480 *    RECORD ID     : 3
1481 *    RECORD VERSION: 0 (or 1)
1482 *
1483 * I would have like to put that stuff in an OEM specific file, but apart for
1484 * the record format information, all commands are really standard 'FRU' command
1485 *
1486 *
1487 * @data:   FRU data
1488 * @offset: start of the current multi record (start of header)
1489 * @len:    len of the current record (excluding header)
1490 * @h:      pointer to record header
1491 * @oh:     pointer to OEM /PICMG header
1492 *
1493 * returns: TRUE if data changed
1494 * returns: FALSE if data not changed
1495 */
1496 #define OEM_KONTRON_INFORMATION_RECORD 3
1497 
1498 #define EDIT_OEM_KONTRON_COMPLETE_ARG_COUNT    12
1499 #define GET_OEM_KONTRON_COMPLETE_ARG_COUNT     5
1500 /*
1501 ./src/ipmitool  fru edit 0
1502 oem 15000 3 0 name instance FIELD1 FIELD2 FIELD3 crc32
1503 */
1504 
1505 #define OEM_KONTRON_SUBCOMMAND_ARG_POS   2
1506 #define OEM_KONTRON_IANA_ARG_POS         3
1507 #define OEM_KONTRON_RECORDID_ARG_POS     4
1508 #define OEM_KONTRON_FORMAT_ARG_POS       5
1509 #define OEM_KONTRON_NAME_ARG_POS         6
1510 #define OEM_KONTRON_INSTANCE_ARG_POS     7
1511 #define OEM_KONTRON_VERSION_ARG_POS      8
1512 #define OEM_KONTRON_BUILDDATE_ARG_POS    9
1513 #define OEM_KONTRON_UPDATEDATE_ARG_POS   10
1514 #define OEM_KONTRON_CRC32_ARG_POS        11
1515 
1516 #define OEM_KONTRON_FIELD_SIZE          8
1517 #define OEM_KONTRON_VERSION_FIELD_SIZE 10
1518 
1519 #ifdef HAVE_PRAGMA_PACK
1520 #pragma pack(1)
1521 #endif
1522 typedef struct OemKontronInformationRecordV0{
1523 	uint8_t field1TypeLength;
1524 	uint8_t field1[OEM_KONTRON_FIELD_SIZE];
1525 	uint8_t field2TypeLength;
1526 	uint8_t field2[OEM_KONTRON_FIELD_SIZE];
1527 	uint8_t field3TypeLength;
1528 	uint8_t field3[OEM_KONTRON_FIELD_SIZE];
1529 	uint8_t crcTypeLength;
1530 	uint8_t crc32[OEM_KONTRON_FIELD_SIZE];
1531 }tOemKontronInformationRecordV0;
1532 #ifdef HAVE_PRAGMA_PACK
1533 #pragma pack(0)
1534 #endif
1535 
1536 
1537 #ifdef HAVE_PRAGMA_PACK
1538 #pragma pack(1)
1539 #endif
1540 typedef struct OemKontronInformationRecordV1{
1541 	uint8_t field1TypeLength;
1542 	uint8_t field1[OEM_KONTRON_VERSION_FIELD_SIZE];
1543 	uint8_t field2TypeLength;
1544 	uint8_t field2[OEM_KONTRON_FIELD_SIZE];
1545 	uint8_t field3TypeLength;
1546 	uint8_t field3[OEM_KONTRON_FIELD_SIZE];
1547 	uint8_t crcTypeLength;
1548 	uint8_t crc32[OEM_KONTRON_FIELD_SIZE];
1549 }tOemKontronInformationRecordV1;
1550 #ifdef HAVE_PRAGMA_PACK
1551 #pragma pack(0)
1552 #endif
1553 
1554 /*
1555 ./src/ipmitool  fru get 0 oem iana 3
1556 
1557 */
1558 
1559 static void ipmi_fru_oemkontron_get( int argc, char ** argv,uint8_t * fru_data,
1560 												int off,int len,
1561 												struct fru_multirec_header *h,
1562 												struct fru_multirec_oem_header *oh)
1563 {
1564 	static int badParams=FALSE;
1565 	int start = off;
1566 	int offset = start;
1567 	int length = len;
1568 	int i;
1569 	offset += sizeof(struct fru_multirec_oem_header);
1570 
1571 	if(!badParams){
1572 		/* the 'OEM' field is already checked in caller */
1573 		if( argc > OEM_KONTRON_SUBCOMMAND_ARG_POS ){
1574 			if(strncmp("oem", argv[OEM_KONTRON_SUBCOMMAND_ARG_POS],3)){
1575 				printf("usage: fru get <id> <oem>\n");
1576 				badParams = TRUE;
1577 				return;
1578 			}
1579 		}
1580 		if( argc<GET_OEM_KONTRON_COMPLETE_ARG_COUNT ){
1581 			printf("usage: oem <iana> <recordid>\n");
1582 			printf("usage: oem 15000 3\n");
1583 			badParams = TRUE;
1584 			return;
1585 		}
1586 	}
1587 
1588 	if(!badParams){
1589 
1590 		if(oh->record_id == OEM_KONTRON_INFORMATION_RECORD ) {
1591 
1592 			uint8_t version;
1593 
1594 			printf("Kontron OEM Information Record\n");
1595 			version = oh->record_version;
1596 
1597 			int blockstart;
1598 			uint8_t blockCount;
1599 			uint8_t blockIndex=0;
1600 
1601 			unsigned int matchInstance = 0;
1602 			uint8_t instance = 0;
1603 
1604 			if (str2uchar(argv[OEM_KONTRON_INSTANCE_ARG_POS], &instance) != 0) {
1605 				lprintf(LOG_ERR,
1606 						"Instance argument '%s' is either invalid or out of range.",
1607 						argv[OEM_KONTRON_INSTANCE_ARG_POS]);
1608 				badParams = TRUE;
1609 				return;
1610 			}
1611 
1612 			blockCount = fru_data[offset++];
1613 
1614 			for(blockIndex=0;blockIndex<blockCount;blockIndex++){
1615 				void * pRecordData;
1616 				uint8_t nameLen;
1617 
1618 				blockstart = offset;
1619 				nameLen = ( fru_data[offset++] &= 0x3F );
1620 				printf("  Name: %*.*s\n",nameLen, nameLen, (const char *)(fru_data+offset));
1621 
1622 				offset+=nameLen;
1623 
1624 				pRecordData = &fru_data[offset];
1625 
1626 				printf("  Record Version: %d\n", version);
1627 				if( version == 0 )
1628 				{
1629 					printf("  Version: %*.*s\n",
1630 						OEM_KONTRON_FIELD_SIZE,
1631 						OEM_KONTRON_FIELD_SIZE,
1632 						((tOemKontronInformationRecordV0 *) pRecordData)->field1);
1633 					printf("  Build Date: %*.*s\n",
1634 						OEM_KONTRON_FIELD_SIZE,
1635 						OEM_KONTRON_FIELD_SIZE,
1636 						((tOemKontronInformationRecordV0 *) pRecordData)->field2);
1637 					printf("  Update Date: %*.*s\n",
1638 						OEM_KONTRON_FIELD_SIZE,
1639 						OEM_KONTRON_FIELD_SIZE,
1640 						((tOemKontronInformationRecordV0 *) pRecordData)->field3);
1641 					printf("  Checksum: %*.*s\n\n",
1642 						OEM_KONTRON_FIELD_SIZE,
1643 						OEM_KONTRON_FIELD_SIZE,
1644 						((tOemKontronInformationRecordV0 *) pRecordData)->crc32);
1645 					matchInstance++;
1646 					offset+= sizeof(tOemKontronInformationRecordV0);
1647 					offset++;
1648 				}
1649 				else if ( version == 1 )
1650 				{
1651 					printf("  Version: %*.*s\n",
1652 						OEM_KONTRON_VERSION_FIELD_SIZE,
1653 						OEM_KONTRON_VERSION_FIELD_SIZE,
1654 						((tOemKontronInformationRecordV1 *) pRecordData)->field1);
1655 					printf("  Build Date: %*.*s\n",
1656 						OEM_KONTRON_FIELD_SIZE,
1657 						OEM_KONTRON_FIELD_SIZE,
1658 						((tOemKontronInformationRecordV1 *) pRecordData)->field2);
1659 					printf("  Update Date: %*.*s\n",
1660 						OEM_KONTRON_FIELD_SIZE,
1661 						OEM_KONTRON_FIELD_SIZE,
1662 						((tOemKontronInformationRecordV1 *) pRecordData)->field3);
1663 					printf("  Checksum: %*.*s\n\n",
1664 						OEM_KONTRON_FIELD_SIZE,
1665 						OEM_KONTRON_FIELD_SIZE,
1666 						((tOemKontronInformationRecordV1 *) pRecordData)->crc32);
1667 					matchInstance++;
1668 					offset+= sizeof(tOemKontronInformationRecordV1);
1669 					offset++;
1670 				}
1671 				else
1672 				{
1673 					printf ("  Unsupported version %d\n",version);
1674 				}
1675 			}
1676 		}
1677 	}
1678 }
1679 
1680 static int ipmi_fru_oemkontron_edit( int argc, char ** argv,uint8_t * fru_data,
1681 												int off,int len,
1682 												struct fru_multirec_header *h,
1683 												struct fru_multirec_oem_header *oh)
1684 {
1685 	static int badParams=FALSE;
1686 	int hasChanged = FALSE;
1687 	int start = off;
1688 	int offset = start;
1689 	int length = len;
1690 	int i;
1691 	uint8_t record_id = 0;
1692 	offset += sizeof(struct fru_multirec_oem_header);
1693 
1694 	if(!badParams){
1695 		/* the 'OEM' field is already checked in caller */
1696 		if( argc > OEM_KONTRON_SUBCOMMAND_ARG_POS ){
1697 			if(strncmp("oem", argv[OEM_KONTRON_SUBCOMMAND_ARG_POS],3)){
1698 				printf("usage: fru edit <id> <oem> <args...>\n");
1699 				badParams = TRUE;
1700 				return hasChanged;
1701 			}
1702 		}
1703 		if( argc<EDIT_OEM_KONTRON_COMPLETE_ARG_COUNT ){
1704 			printf("usage: oem <iana> <recordid> <format> <args...>\n");
1705 			printf("usage: oem 15000 3 0 <name> <instance> <field1>"\
1706 					" <field2> <field3> <crc32>\n");
1707 			badParams = TRUE;
1708 			return hasChanged;
1709 		}
1710 		if (str2uchar(argv[OEM_KONTRON_RECORDID_ARG_POS], &record_id) != 0) {
1711 			lprintf(LOG_ERR,
1712 					"Record ID argument '%s' is either invalid or out of range.",
1713 					argv[OEM_KONTRON_RECORDID_ARG_POS]);
1714 			badParams = TRUE;
1715 			return hasChanged;
1716 		}
1717 		if (record_id == OEM_KONTRON_INFORMATION_RECORD) {
1718 			for(i=OEM_KONTRON_VERSION_ARG_POS;i<=OEM_KONTRON_CRC32_ARG_POS;i++){
1719 				if( (strlen(argv[i]) != OEM_KONTRON_FIELD_SIZE) &&
1720 					(strlen(argv[i]) != OEM_KONTRON_VERSION_FIELD_SIZE)) {
1721 					printf("error: version fields must have %d characters\n",
1722 										OEM_KONTRON_FIELD_SIZE);
1723 					badParams = TRUE;
1724 					return hasChanged;
1725 				}
1726 			}
1727 		}
1728 	}
1729 
1730 	if(!badParams){
1731 
1732 		if(oh->record_id == OEM_KONTRON_INFORMATION_RECORD ) {
1733 			uint8_t formatVersion = 0;
1734 			uint8_t version;
1735 
1736 			if (str2uchar(argv[OEM_KONTRON_FORMAT_ARG_POS], &formatVersion) != 0) {
1737 				lprintf(LOG_ERR,
1738 						"Format argument '%s' is either invalid or out of range.",
1739 						argv[OEM_KONTRON_FORMAT_ARG_POS]);
1740 				badParams = TRUE;
1741 				return hasChanged;
1742 			}
1743 
1744 			printf("   Kontron OEM Information Record\n");
1745 			version = oh->record_version;
1746 
1747 			if( version == formatVersion  ){
1748 				int blockstart;
1749 				uint8_t blockCount;
1750 				uint8_t blockIndex=0;
1751 
1752 				uint8_t matchInstance = 0;
1753 				uint8_t instance = 0;
1754 
1755 				if (str2uchar(argv[OEM_KONTRON_INSTANCE_ARG_POS], &instance) != 0) {
1756 					lprintf(LOG_ERR,
1757 							"Instance argument '%s' is either invalid or out of range.",
1758 							argv[OEM_KONTRON_INSTANCE_ARG_POS]);
1759 					badParams = TRUE;
1760 					return hasChanged;
1761 				}
1762 
1763 				blockCount = fru_data[offset++];
1764 				printf("   blockCount: %d\n",blockCount);
1765 
1766 				for(blockIndex=0;blockIndex<blockCount;blockIndex++){
1767 					void * pRecordData;
1768 					uint8_t nameLen;
1769 
1770 					blockstart = offset;
1771 
1772 					nameLen = ( fru_data[offset++] & 0x3F );
1773 
1774 					if( version == 0 || version == 1 )
1775 					{
1776 						if(!strncmp((char *)argv[OEM_KONTRON_NAME_ARG_POS],
1777 						(const char *)(fru_data+offset),nameLen)&& (matchInstance == instance)){
1778 
1779 							printf ("Found : %s\n",argv[OEM_KONTRON_NAME_ARG_POS]);
1780 							offset+=nameLen;
1781 
1782 							pRecordData =  &fru_data[offset];
1783 
1784 							if( version == 0 )
1785 							{
1786 								memcpy( ((tOemKontronInformationRecordV0 *)
1787 															pRecordData)->field1 ,
1788 								argv[OEM_KONTRON_VERSION_ARG_POS] ,
1789 								OEM_KONTRON_FIELD_SIZE);
1790 								memcpy( ((tOemKontronInformationRecordV0 *)
1791 															pRecordData)->field2 ,
1792 								argv[OEM_KONTRON_BUILDDATE_ARG_POS],
1793 								OEM_KONTRON_FIELD_SIZE);
1794 								memcpy( ((tOemKontronInformationRecordV0 *)
1795 															pRecordData)->field3 ,
1796 								argv[OEM_KONTRON_UPDATEDATE_ARG_POS],
1797 								OEM_KONTRON_FIELD_SIZE);
1798 								memcpy( ((tOemKontronInformationRecordV0 *)
1799 															pRecordData)->crc32 ,
1800 							argv[OEM_KONTRON_CRC32_ARG_POS] ,
1801 							OEM_KONTRON_FIELD_SIZE);
1802 							}
1803 							else
1804 							{
1805 								memcpy( ((tOemKontronInformationRecordV1 *)
1806 															pRecordData)->field1 ,
1807 								argv[OEM_KONTRON_VERSION_ARG_POS] ,
1808 								OEM_KONTRON_VERSION_FIELD_SIZE);
1809 								memcpy( ((tOemKontronInformationRecordV1 *)
1810 															pRecordData)->field2 ,
1811 								argv[OEM_KONTRON_BUILDDATE_ARG_POS],
1812 								OEM_KONTRON_FIELD_SIZE);
1813 								memcpy( ((tOemKontronInformationRecordV1 *)
1814 															pRecordData)->field3 ,
1815 								argv[OEM_KONTRON_UPDATEDATE_ARG_POS],
1816 								OEM_KONTRON_FIELD_SIZE);
1817 								memcpy( ((tOemKontronInformationRecordV1 *)
1818 															pRecordData)->crc32 ,
1819 							argv[OEM_KONTRON_CRC32_ARG_POS] ,
1820 							OEM_KONTRON_FIELD_SIZE);
1821 							}
1822 
1823 							matchInstance++;
1824 							hasChanged = TRUE;
1825 						}
1826 						else if(!strncmp((char *)argv[OEM_KONTRON_NAME_ARG_POS],
1827 							(const char *)(fru_data+offset), nameLen)){
1828 							printf ("Skipped : %s  [instance %d]\n",argv[OEM_KONTRON_NAME_ARG_POS],
1829 									(unsigned int)matchInstance);
1830 							matchInstance++;
1831 							offset+=nameLen;
1832 						}
1833 						else {
1834 							offset+=nameLen;
1835 						}
1836 
1837 						if( version == 0 )
1838 						{
1839 							offset+= sizeof(tOemKontronInformationRecordV0);
1840 						}
1841 						else
1842 						{
1843 							offset+= sizeof(tOemKontronInformationRecordV1);
1844 						}
1845 						offset++;
1846 					}
1847 					else
1848 					{
1849 						printf ("  Unsupported version %d\n",version);
1850 					}
1851 				}
1852 			}
1853 			else{
1854 				printf("   Version: %d\n",version);
1855 			}
1856 		}
1857 		if( hasChanged ){
1858 
1859 			uint8_t record_checksum =0;
1860 			uint8_t header_checksum =0;
1861 			int index;
1862 
1863 			lprintf(LOG_DEBUG,"Initial record checksum : %x",h->record_checksum);
1864 			lprintf(LOG_DEBUG,"Initial header checksum : %x",h->header_checksum);
1865 			for(index=0;index<length;index++){
1866 				record_checksum+=  fru_data[start+index];
1867 			}
1868 			/* Update Record checksum */
1869 			h->record_checksum =  ~record_checksum + 1;
1870 
1871 
1872 			for(index=0;index<(sizeof(struct fru_multirec_header) -1);index++){
1873 				uint8_t data= *( (uint8_t *)h+ index);
1874 				header_checksum+=data;
1875 			}
1876 			/* Update header checksum */
1877 			h->header_checksum =  ~header_checksum + 1;
1878 
1879 			lprintf(LOG_DEBUG,"Final record checksum : %x",h->record_checksum);
1880 			lprintf(LOG_DEBUG,"Final header checksum : %x",h->header_checksum);
1881 
1882 			/* write back data */
1883 		}
1884 	}
1885 
1886 	return hasChanged;
1887 }
1888 
1889 /* ipmi_fru_picmg_ext_edit  -  Query new values to replace original FRU content
1890 *
1891 * @data:   FRU data
1892 * @offset: start of the current multi record (start of header)
1893 * @len:    len of the current record (excluding header)
1894 * @h:      pointer to record header
1895 * @oh:     pointer to OEM /PICMG header
1896 *
1897 * returns: TRUE if data changed
1898 * returns: FALSE if data not changed
1899 */
1900 static int ipmi_fru_picmg_ext_edit(uint8_t * fru_data,
1901 												int off,int len,
1902 												struct fru_multirec_header *h,
1903 												struct fru_multirec_oem_header *oh)
1904 {
1905 	int hasChanged = FALSE;
1906 	int start = off;
1907 	int offset = start;
1908 	int length = len;
1909 	offset += sizeof(struct fru_multirec_oem_header);
1910 
1911 	switch (oh->record_id)
1912 	{
1913 		case FRU_AMC_ACTIVATION:
1914 			printf("    FRU_AMC_ACTIVATION\n");
1915 			{
1916 				int index=offset;
1917 				uint16_t max_current;
1918 
1919 				max_current = fru_data[offset];
1920 				max_current |= fru_data[++offset]<<8;
1921 
1922 				printf("      Maximum Internal Current(@12V): %.2f A (0x%02x)\n",
1923 								(float)max_current / 10.0f, max_current);
1924 
1925 				if( ipmi_fru_query_new_value(fru_data,index,2) ){
1926 					max_current = fru_data[index];
1927 					max_current |= fru_data[++index]<<8;
1928 					printf("      New Maximum Internal Current(@12V): %.2f A (0x%02x)\n",
1929 								(float)max_current / 10.0f, max_current);
1930 					hasChanged = TRUE;
1931 
1932 				}
1933 
1934 				printf("      Module Activation Readiness:       %i sec.\n", fru_data[++offset]);
1935 				printf("      Descriptor Count: %i\n", fru_data[++offset]);
1936 				printf("\n");
1937 
1938 				for (++offset;
1939 					offset < (off + length);
1940 					offset += sizeof(struct fru_picmgext_activation_record)) {
1941 					struct fru_picmgext_activation_record * a =
1942 						(struct fru_picmgext_activation_record *) &fru_data[offset];
1943 
1944 					printf("        IPMB-Address:         0x%x\n", a->ibmb_addr);
1945 					printf("        Max. Module Current:  %.2f A\n", (float)a->max_module_curr / 10.0f);
1946 
1947 					printf("\n");
1948 				}
1949 			}
1950 			break;
1951 
1952 		case FRU_AMC_CURRENT:
1953 			printf("    FRU_AMC_CURRENT\n");
1954 			{
1955 				int index=offset;
1956 				unsigned char current;
1957 
1958 				current = fru_data[index];
1959 
1960 				printf("      Current draw(@12V): %.2f A (0x%02x)\n",
1961 								(float)current / 10.0f, current);
1962 
1963 				if( ipmi_fru_query_new_value(fru_data, index, 1) ){
1964 					current = fru_data[index];
1965 
1966 					printf("      New Current draw(@12V): %.2f A (0x%02x)\n",
1967 								(float)current / 10.0f, current);
1968 					hasChanged = TRUE;
1969 				}
1970 			}
1971 			break;
1972 	}
1973 
1974 	if( hasChanged ){
1975 
1976 		uint8_t record_checksum =0;
1977 		uint8_t header_checksum =0;
1978 		int index;
1979 
1980 		lprintf(LOG_DEBUG,"Initial record checksum : %x",h->record_checksum);
1981 		lprintf(LOG_DEBUG,"Initial header checksum : %x",h->header_checksum);
1982 		for(index=0;index<length;index++){
1983 			record_checksum+=  fru_data[start+index];
1984 		}
1985 		/* Update Record checksum */
1986 		h->record_checksum =  ~record_checksum + 1;
1987 
1988 
1989 		for(index=0;index<(sizeof(struct fru_multirec_header) -1);index++){
1990 			uint8_t data= *( (uint8_t *)h+ index);
1991 			header_checksum+=data;
1992 		}
1993 		/* Update header checksum */
1994 		h->header_checksum =  ~header_checksum + 1;
1995 
1996 		lprintf(LOG_DEBUG,"Final record checksum : %x",h->record_checksum);
1997 		lprintf(LOG_DEBUG,"Final header checksum : %x",h->header_checksum);
1998 
1999 		/* write back data */
2000 	}
2001 
2002 	return hasChanged;
2003 }
2004 
2005 /* ipmi_fru_picmg_ext_print  - prints OEM fru record (PICMG)
2006 *
2007 * @fru_data:  FRU data
2008 * @offset:    offset of the bytes to be modified in data
2009 * @length:    size of the record
2010 *
2011 * returns : n/a
2012 */
2013 static void ipmi_fru_picmg_ext_print(uint8_t * fru_data, int off, int length)
2014 {
2015 	struct fru_multirec_oem_header *h;
2016 	int guid_count;
2017 	int offset = off;
2018 	int start_offset = off;
2019 	int i;
2020 
2021 	h = (struct fru_multirec_oem_header *) &fru_data[offset];
2022 	offset += sizeof(struct fru_multirec_oem_header);
2023 
2024 	switch (h->record_id)
2025 	{
2026 		case FRU_PICMG_BACKPLANE_P2P:
2027 		{
2028 			uint8_t index;
2029 			unsigned int data;
2030 			struct fru_picmgext_slot_desc *slot_d;
2031 
2032 			slot_d =
2033 				(struct fru_picmgext_slot_desc*)&fru_data[offset];
2034 			offset += sizeof(struct fru_picmgext_slot_desc);
2035 			printf("    FRU_PICMG_BACKPLANE_P2P\n");
2036 
2037 			while (offset <= (start_offset+length)) {
2038 				printf("\n");
2039 				printf("    Channel Type:  ");
2040 				switch (slot_d->chan_type)
2041 				{
2042 					case 0x00:
2043 					case 0x07:
2044 						printf("PICMG 2.9\n");
2045 						break;
2046 					case 0x08:
2047 						printf("Single Port Fabric IF\n");
2048 						break;
2049 					case 0x09:
2050 						printf("Double Port Fabric IF\n");
2051 						break;
2052 					case 0x0a:
2053 						printf("Full Channel Fabric IF\n");
2054 						break;
2055 					case 0x0b:
2056 						printf("Base IF\n");
2057 						break;
2058 					case 0x0c:
2059 						printf("Update Channel IF\n");
2060 						break;
2061 					case 0x0d:
2062 						printf("ShMC Cross Connect\n");
2063 						break;
2064 					default:
2065 						printf("Unknown IF (0x%x)\n",
2066 								slot_d->chan_type);
2067 						break;
2068 				}
2069 				printf("    Slot Addr.   : %02x\n",
2070 						slot_d->slot_addr );
2071 				printf("    Channel Count: %i\n",
2072 						slot_d->chn_count);
2073 
2074 				for (index = 0;
2075 						index < (slot_d->chn_count);
2076 						index++) {
2077 					struct fru_picmgext_chn_desc *d;
2078 					data = (fru_data[offset+0]) |
2079 						(fru_data[offset+1] << 8) |
2080 						(fru_data[offset+2] << 16);
2081 					d = (struct fru_picmgext_chn_desc *)&data;
2082 					if (verbose) {
2083 						printf( "       "
2084 								"Chn: %02x  ->  "
2085 								"Chn: %02x in "
2086 								"Slot: %02x\n",
2087 								d->local_chn,
2088 								d->remote_chn,
2089 								d->remote_slot);
2090 					}
2091 					offset += FRU_PICMGEXT_CHN_DESC_RECORD_SIZE;
2092 				}
2093 				slot_d = (struct fru_picmgext_slot_desc*)&fru_data[offset];
2094 				offset += sizeof(struct fru_picmgext_slot_desc);
2095 			}
2096 		}
2097 		break;
2098 
2099 		case FRU_PICMG_ADDRESS_TABLE:
2100 		{
2101 			unsigned int hwaddr;
2102 			unsigned int sitetype;
2103 			unsigned int sitenum;
2104 			unsigned int entries;
2105 			unsigned int i;
2106 			char *picmg_site_type_strings[] = {
2107 					"AdvancedTCA Board",
2108 					"Power Entry",
2109 					"Shelf FRU Information",
2110 					"Dedicated ShMC",
2111 					"Fan Tray",
2112 					"Fan Filter Tray",
2113 					"Alarm",
2114 					"AdvancedMC Module",
2115 					"PMC",
2116 					"Rear Transition Module"};
2117 
2118 
2119 			printf("    FRU_PICMG_ADDRESS_TABLE\n");
2120 			printf("      Type/Len:  0x%02x\n", fru_data[offset++]);
2121 			printf("      Shelf Addr: ");
2122 			for (i=0;i<20;i++) {
2123 				printf("0x%02x ", fru_data[offset++]);
2124 			}
2125 			printf("\n");
2126 
2127 			entries = fru_data[offset++];
2128 			printf("      Addr Table Entries: 0x%02x\n", entries);
2129 
2130 			for (i=0; i<entries; i++) {
2131 				hwaddr = fru_data[offset];
2132 				sitenum = fru_data[offset + 1];
2133 				sitetype = fru_data[offset + 2];
2134 				printf(
2135 						"        HWAddr: 0x%02x (0x%02x) SiteNum: %d SiteType: 0x%02x %s\n",
2136 						hwaddr, hwaddr * 2,
2137 						sitenum, sitetype,
2138 						(sitetype < 0xa) ?
2139 						picmg_site_type_strings[sitetype] :
2140 						"Reserved");
2141 				offset += 3;
2142 			}
2143 		}
2144 		break;
2145 
2146 		case FRU_PICMG_SHELF_POWER_DIST:
2147 		{
2148 			unsigned int entries;
2149 			unsigned int feeds;
2150 			unsigned int feedcnt;
2151 			unsigned int hwaddr;
2152 			unsigned int i;
2153 			unsigned int id;
2154 			unsigned int j;
2155 			unsigned int maxext;
2156 			unsigned int maxint;
2157 			unsigned int minexp;
2158 
2159 			printf("    FRU_PICMG_SHELF_POWER_DIST\n");
2160 
2161 			feeds = fru_data[offset++];
2162 			printf("      Number of Power Feeds:   0x%02x\n",
2163 					feeds);
2164 
2165 			for (i=0; i<feeds; i++) {
2166 				printf("    Feed %d:\n", i);
2167 				maxext = fru_data[offset] |
2168 					(fru_data[offset+1] << 8);
2169 				offset += 2;
2170 				maxint = fru_data[offset] |
2171 					(fru_data[offset+1] << 8);
2172 				offset += 2;
2173 				minexp = fru_data[offset];
2174 				offset += 1;
2175 				entries = fru_data[offset];
2176 				offset += 1;
2177 
2178 				printf(
2179 						"      Max External Current:   %d.%d Amps (0x%04x)\n",
2180 						maxext / 10, maxext % 10, maxext);
2181 				if (maxint < 0xffff) {
2182 					printf(
2183 							"      Max Internal Current:   %d.%d Amps (0x%04x)\n",
2184 							maxint / 10, maxint % 10,
2185 							maxint);
2186 				} else {
2187 					printf(
2188 							"      Max Internal Current:   Not Specified\n");
2189 				}
2190 
2191 				if (minexp >= 0x48 && minexp <= 0x90) {
2192 					printf(
2193 							"      Min Expected Voltage:   -%02d.%dV\n",
2194 							minexp / 2, (minexp % 2) * 5);
2195 				} else {
2196 					printf(
2197 							"      Min Expected Voltage:   -%dV (actual invalid value 0x%x)\n",
2198 							36, minexp);
2199 				}
2200 				for (j=0; j < entries; j++) {
2201 					hwaddr = fru_data[offset++];
2202 					id = fru_data[offset++];
2203 					printf(
2204 							"        FRU HW Addr: 0x%02x (0x%02x)",
2205 							hwaddr, hwaddr * 2);
2206 					printf(
2207 							"   FRU ID: 0x%02x\n",
2208 							id);
2209 				}
2210 			}
2211 		}
2212 		break;
2213 
2214 		case FRU_PICMG_SHELF_ACTIVATION:
2215 		{
2216 			unsigned int i;
2217 			unsigned int count = 0;
2218 
2219 			printf("    FRU_PICMG_SHELF_ACTIVATION\n");
2220 			printf(
2221 					"      Allowance for FRU Act Readiness:   0x%02x\n",
2222 					fru_data[offset++]);
2223 
2224 			count = fru_data[offset++];
2225 			printf(
2226 					"      FRU activation and Power Desc Cnt: 0x%02x\n",
2227 					count);
2228 
2229 			for (i=0; i<count; i++) {
2230 				printf("         HW Addr: 0x%02x ",
2231 						fru_data[offset++]);
2232 				printf("         FRU ID: 0x%02x ",
2233 						fru_data[offset++]);
2234 				printf("         Max FRU Power: 0x%04x ",
2235 						fru_data[offset+0] |
2236 						(fru_data[offset+1]<<8));
2237 				offset += 2;
2238 				printf("         Config: 0x%02x \n",
2239 						fru_data[offset++]);
2240 			}
2241 		}
2242 		break;
2243 
2244 		case FRU_PICMG_SHMC_IP_CONN:
2245 			printf("    FRU_PICMG_SHMC_IP_CONN\n");
2246 			break;
2247 
2248 		case FRU_PICMG_BOARD_P2P:
2249 			printf("    FRU_PICMG_BOARD_P2P\n");
2250 
2251 			guid_count = fru_data[offset++];
2252 			printf("      GUID count: %2d\n", guid_count);
2253 			for (i = 0 ; i < guid_count; i++ ) {
2254 				int j;
2255 				printf("        GUID [%2d]: 0x", i);
2256 
2257 				for (j=0; j < sizeof(struct fru_picmgext_guid);
2258 						j++) {
2259 					printf("%02x", fru_data[offset+j]);
2260 				}
2261 
2262 				printf("\n");
2263 				offset += sizeof(struct fru_picmgext_guid);
2264 			}
2265 			printf("\n");
2266 
2267 			for (; offset < off + length;
2268 					offset += sizeof(struct fru_picmgext_link_desc)) {
2269 
2270 				/* to solve little endian /big endian problem */
2271 				struct fru_picmgext_link_desc *d;
2272 				unsigned int data = (fru_data[offset+0]) |
2273 					(fru_data[offset+1] << 8) |
2274 					(fru_data[offset+2] << 16) |
2275 					(fru_data[offset+3] << 24);
2276 				d = (struct fru_picmgext_link_desc *) &data;
2277 
2278 				printf("      Link Grouping ID:     0x%02x\n",
2279 						d->grouping);
2280 				printf("      Link Type Extension:  0x%02x - ",
2281 						d->ext);
2282 				if (d->type == FRU_PICMGEXT_LINK_TYPE_BASE) {
2283 					switch (d->ext)
2284 					{
2285 						case 0:
2286 							printf("10/100/1000BASE-T Link (four-pair)\n");
2287 							break;
2288 						case 1:
2289 							printf("ShMC Cross-connect (two-pair)\n");
2290 							break;
2291 						default:
2292 							printf("Unknwon\n");
2293 							break;
2294 					}
2295 				} else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET) {
2296 					switch (d->ext)
2297 					{
2298 						case 0:
2299 							printf("Fixed 1000Base-BX\n");
2300 							break;
2301 						case 1:
2302 							printf("Fixed 10GBASE-BX4 [XAUI]\n");
2303 							break;
2304 						case 2:
2305 							printf("FC-PI\n");
2306 							break;
2307 						default:
2308 							printf("Unknwon\n");
2309 							break;
2310 					}
2311 				} else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND) {
2312 					printf("Unknwon\n");
2313 				} else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR) {
2314 					printf("Unknwon\n");
2315 				} else if (d->type == FRU_PICMGEXT_LINK_TYPE_PCIE) {
2316 					printf("Unknwon\n");
2317 				} else {
2318 					printf("Unknwon\n");
2319 				}
2320 
2321 				printf("      Link Type:            0x%02x - ",
2322 						d->type);
2323 				if (d->type == 0 || d->type == 0xff) {
2324 					printf("Reserved\n");
2325 				}
2326 				else if (d->type >= 0x06 && d->type <= 0xef) {
2327 					printf("Reserved\n");
2328 				}
2329 				else if (d->type >= 0xf0 && d->type <= 0xfe) {
2330 					printf("OEM GUID Definition\n");
2331 				}
2332 				else {
2333 					switch (d->type)
2334 					{
2335 						case FRU_PICMGEXT_LINK_TYPE_BASE:
2336 							printf("PICMG 3.0 Base Interface 10/100/1000\n");
2337 							break;
2338 						case FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET:
2339 							printf("PICMG 3.1 Ethernet Fabric Interface\n");
2340 							break;
2341 						case FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND:
2342 							printf("PICMG 3.2 Infiniband Fabric Interface\n");
2343 							break;
2344 						case FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR:
2345 							printf("PICMG 3.3 Star Fabric Interface\n");
2346 							break;
2347 						case  FRU_PICMGEXT_LINK_TYPE_PCIE:
2348 							printf("PICMG 3.4 PCI Express Fabric Interface\n");
2349 							break;
2350 						default:
2351 							printf("Invalid\n");
2352 							break;
2353 					}
2354 				}
2355 				printf("      Link Designator: \n");
2356 				printf("        Port Flag:            0x%02x\n",
2357 						d->desig_port);
2358 				printf("        Interface:            0x%02x - ",
2359 						d->desig_if);
2360 				switch (d->desig_if)
2361 				{
2362 					case FRU_PICMGEXT_DESIGN_IF_BASE:
2363 						printf("Base Interface\n");
2364 						break;
2365 					case FRU_PICMGEXT_DESIGN_IF_FABRIC:
2366 						printf("Fabric Interface\n");
2367 						break;
2368 					case FRU_PICMGEXT_DESIGN_IF_UPDATE_CHANNEL:
2369 						printf("Update Channel\n");
2370 						break;
2371 					case FRU_PICMGEXT_DESIGN_IF_RESERVED:
2372 						printf("Reserved\n");
2373 						break;
2374 					default:
2375 						printf("Invalid");
2376 						break;
2377 				}
2378 				printf("        Channel Number:       0x%02x\n",
2379 						d->desig_channel);
2380 				printf("\n");
2381 			}
2382 
2383 			break;
2384 
2385 		case FRU_AMC_CURRENT:
2386 		{
2387 			unsigned char current;
2388 			printf("    FRU_AMC_CURRENT\n");
2389 
2390 			current = fru_data[offset];
2391 			printf("      Current draw(@12V): %.2f A [ %.2f Watt ]\n",
2392 					(float)current / 10.0f,
2393 					(float)current / 10.0f * 12.0f);
2394 			printf("\n");
2395 		}
2396 		break;
2397 
2398 		case FRU_AMC_ACTIVATION:
2399 			printf("    FRU_AMC_ACTIVATION\n");
2400 			{
2401 				uint16_t max_current;
2402 
2403 				max_current = fru_data[offset];
2404 				max_current |= fru_data[++offset]<<8;
2405 				printf("      Maximum Internal Current(@12V): %.2f A [ %.2f Watt ]\n",
2406 						(float)max_current / 10.0f,
2407 						(float)max_current / 10.0f * 12.0f);
2408 
2409 				printf("      Module Activation Readiness:    %i sec.\n", fru_data[++offset]);
2410 				printf("      Descriptor Count: %i\n", fru_data[++offset]);
2411 				printf("\n");
2412 
2413 				for(++offset; offset < off + length;
2414 						offset += sizeof(struct fru_picmgext_activation_record))
2415 				{
2416 					struct fru_picmgext_activation_record *a;
2417 					a = (struct fru_picmgext_activation_record *)&fru_data[offset];
2418 					printf("        IPMB-Address:         0x%x\n",
2419 							a->ibmb_addr);
2420 					printf("        Max. Module Current:  %.2f A\n",
2421 							(float)a->max_module_curr / 10.0f);
2422 					printf("\n");
2423 				}
2424 			}
2425 			break;
2426 
2427 		case FRU_AMC_CARRIER_P2P:
2428 			{
2429 				uint16_t index;
2430 				printf("    FRU_CARRIER_P2P\n");
2431 				for(; offset < off + length; ) {
2432 					struct fru_picmgext_carrier_p2p_record * h =
2433 						(struct fru_picmgext_carrier_p2p_record *)&fru_data[offset];
2434 					printf("\n");
2435 					printf("      Resource ID:      %i",
2436 							(h->resource_id & 0x07));
2437 						printf("  Type: ");
2438 					if ((h->resource_id>>7) == 1) {
2439 						printf("AMC\n");
2440 					} else {
2441 						printf("Local\n");
2442 					}
2443 					printf("      Descriptor Count: %i\n",
2444 							h->p2p_count);
2445 					offset += sizeof(struct fru_picmgext_carrier_p2p_record);
2446 					for (index = 0; index < h->p2p_count; index++) {
2447 						/* to solve little endian /big endian problem */
2448 						unsigned char data[3];
2449 						struct fru_picmgext_carrier_p2p_descriptor * desc;
2450 # ifndef WORDS_BIGENDIAN
2451 						data[0] = fru_data[offset+0];
2452 						data[1] = fru_data[offset+1];
2453 						data[2] = fru_data[offset+2];
2454 # else
2455 						data[0] = fru_data[offset+2];
2456 						data[1] = fru_data[offset+1];
2457 						data[2] = fru_data[offset+0];
2458 # endif
2459 						desc = (struct fru_picmgext_carrier_p2p_descriptor*)&data;
2460 						printf("        Port: %02d\t->  Remote Port: %02d\t",
2461 								desc->local_port, desc->remote_port);
2462 						if ((desc->remote_resource_id >> 7) == 1) {
2463 							printf("[ AMC   ID: %02d ]\n",
2464 									desc->remote_resource_id & 0x0F);
2465 						} else {
2466 							printf("[ local ID: %02d ]\n",
2467 									desc->remote_resource_id & 0x0F);
2468 						}
2469 						offset += sizeof(struct fru_picmgext_carrier_p2p_descriptor);
2470 					}
2471 				}
2472 			}
2473 			break;
2474 
2475 		case FRU_AMC_P2P:
2476 			{
2477 				unsigned int index;
2478 				unsigned char channel_count;
2479 				struct fru_picmgext_amc_p2p_record * h;
2480 				printf("    FRU_AMC_P2P\n");
2481 				guid_count = fru_data[offset];
2482 				printf("      GUID count: %2d\n", guid_count);
2483 				for (i = 0 ; i < guid_count; i++) {
2484 					int j;
2485 					printf("        GUID %2d: ", i);
2486 					for (j=0; j < sizeof(struct fru_picmgext_guid);
2487 							j++) {
2488 						printf("%02x", fru_data[offset+j]);
2489 						offset += sizeof(struct fru_picmgext_guid);
2490 						printf("\n");
2491 					}
2492 					h = (struct fru_picmgext_amc_p2p_record *)&fru_data[++offset];
2493 					printf("      %s",
2494 							(h->record_type ?
2495 							 "AMC Module:" : "On-Carrier Device"));
2496 					printf("   Resource ID: %i\n", h->resource_id);
2497 					offset += sizeof(struct fru_picmgext_amc_p2p_record);
2498 					channel_count = fru_data[offset++];
2499 					printf("       Descriptor Count: %i\n",
2500 							channel_count);
2501 					for (index = 0; index < channel_count; index++) {
2502 						unsigned int data;
2503 						struct fru_picmgext_amc_channel_desc_record *d;
2504 						/* pack the data in little endian format.
2505 						 * Stupid intel...
2506 						 */
2507 						data = fru_data[offset] |
2508 							(fru_data[offset + 1] << 8) |
2509 							(fru_data[offset + 2] << 16);
2510 						d = (struct fru_picmgext_amc_channel_desc_record *)&data;
2511 						printf("        Lane 0 Port: %i\n",
2512 								d->lane0port);
2513 						printf("        Lane 1 Port: %i\n",
2514 								d->lane1port);
2515 						printf("        Lane 2 Port: %i\n",
2516 								d->lane2port);
2517 						printf("        Lane 3 Port: %i\n\n",
2518 								d->lane3port);
2519 						offset += FRU_PICMGEXT_AMC_CHANNEL_DESC_RECORD_SIZE;
2520 					}
2521 					for (; offset < off + length;) {
2522 						unsigned int data[2];
2523 						struct fru_picmgext_amc_link_desc_record *l;
2524 						l = (struct fru_picmgext_amc_link_desc_record *)&data[0];
2525 						data[0] = fru_data[offset] |
2526 							(fru_data[offset + 1] << 8) |
2527 							(fru_data[offset + 2] << 16) |
2528 							(fru_data[offset + 3] << 24);
2529 						data[1] = fru_data[offset + 4];
2530 						printf( "      Link Designator:  Channel ID: %i\n"
2531 								"            Port Flag 0: %s%s%s%s\n",
2532 								l->channel_id,
2533 								(l->port_flag_0)?"o":"-",
2534 								(l->port_flag_1)?"o":"-",
2535 								(l->port_flag_2)?"o":"-",
2536 								(l->port_flag_3)?"o":"-"  );
2537 						switch (l->type) {
2538 							case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE:
2539 								/* AMC.1 */
2540 								printf( "        Link Type:       %02x - "
2541 										"AMC.1 PCI Express\n", l->type);
2542 								switch (l->type_ext) {
2543 									case AMC_LINK_TYPE_EXT_PCIE_G1_NSSC:
2544 										printf( "        Link Type Ext:   %i - "
2545 												" Gen 1 capable - non SSC\n",
2546 												l->type_ext);
2547 									break;
2548 									case AMC_LINK_TYPE_EXT_PCIE_G1_SSC:
2549 										printf( "        Link Type Ext:   %i - "
2550 												" Gen 1 capable - SSC\n",
2551 												l->type_ext);
2552 										break;
2553 									case AMC_LINK_TYPE_EXT_PCIE_G2_NSSC:
2554 										printf( "        Link Type Ext:   %i - "
2555 												" Gen 2 capable - non SSC\n",
2556 												l->type_ext);
2557 										break;
2558 									case AMC_LINK_TYPE_EXT_PCIE_G2_SSC:
2559 										printf( "        Link Type Ext:   %i - "
2560 												" Gen 2 capable - SSC\n",
2561 												l->type_ext);
2562 										break;
2563 									default:
2564 										printf( "        Link Type Ext:   %i - "
2565 												" Invalid\n",
2566 												l->type_ext);
2567 										break;
2568 								}
2569 								break;
2570 
2571 							case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS1:
2572 							case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS2:
2573 								/* AMC.1 */
2574 								printf( "        Link Type:       %02x - "
2575 										"AMC.1 PCI Express Advanced Switching\n",
2576 										l->type);
2577 								printf("        Link Type Ext:   %i\n",
2578 										l->type_ext);
2579 								break;
2580 
2581 							case FRU_PICMGEXT_AMC_LINK_TYPE_ETHERNET:
2582 								/* AMC.2 */
2583 								printf( "        Link Type:       %02x - "
2584 										"AMC.2 Ethernet\n",
2585 										l->type);
2586 								switch (l->type_ext) {
2587 									case AMC_LINK_TYPE_EXT_ETH_1000_BX:
2588 										printf( "        Link Type Ext:   %i - "
2589 												" 1000Base-Bx (SerDES Gigabit) Ethernet Link\n",
2590 												l->type_ext);
2591 										break;
2592 
2593 									case AMC_LINK_TYPE_EXT_ETH_10G_XAUI:
2594 										printf( "        Link Type Ext:   %i - "
2595 												" 10Gbit XAUI Ethernet Link\n",
2596 										l->type_ext);
2597 										break;
2598 
2599 									default:
2600 										printf( "        Link Type Ext:   %i - "
2601 												" Invalid\n",
2602 												l->type_ext);
2603 										break;
2604 								}
2605 								break;
2606 
2607 							case FRU_PICMGEXT_AMC_LINK_TYPE_STORAGE:
2608 								/* AMC.3 */
2609 								printf( "        Link Type:       %02x - "
2610 										"AMC.3 Storage\n",
2611 										l->type);
2612 								switch (l->type_ext) {
2613 									case AMC_LINK_TYPE_EXT_STORAGE_FC:
2614 										printf( "        Link Type Ext:   %i - "
2615 												" Fibre Channel\n",
2616 												l->type_ext);
2617 										break;
2618 
2619 									case AMC_LINK_TYPE_EXT_STORAGE_SATA:
2620 										printf( "        Link Type Ext:   %i - "
2621 												" Serial ATA\n",
2622 												l->type_ext);
2623 										break;
2624 
2625 									case AMC_LINK_TYPE_EXT_STORAGE_SAS:
2626 										printf( "        Link Type Ext:   %i - "
2627 												" Serial Attached SCSI\n",
2628 												l->type_ext);
2629 										break;
2630 
2631 									default:
2632 										printf( "        Link Type Ext:   %i - "
2633 												" Invalid\n",
2634 												l->type_ext);
2635 										break;
2636 								}
2637 								break;
2638 
2639 							case FRU_PICMGEXT_AMC_LINK_TYPE_RAPIDIO:
2640 								/* AMC.4 */
2641 								printf( "        Link Type:       %02x - "
2642 										"AMC.4 Serial Rapid IO\n",
2643 										l->type);
2644 								printf("        Link Type Ext:   %i\n",
2645 										l->type_ext);
2646 								break;
2647 
2648 							default:
2649 								printf( "        Link Type:       %02x - "
2650 										"reserved or OEM GUID",
2651 										l->type);
2652 								printf("        Link Type Ext:   %i\n",
2653 										l->type_ext);
2654 								break;
2655 						}
2656 
2657 						printf("        Link group Id:   %i\n",
2658 								l->group_id);
2659 						printf("        Link Asym Match: %i\n\n",
2660 								l->asym_match);
2661 						offset += FRU_PICMGEXT_AMC_LINK_DESC_RECORD_SIZE;
2662 					}
2663 				}
2664 			}
2665 			break;
2666 
2667 		case FRU_AMC_CARRIER_INFO:
2668 		{
2669 			unsigned char extVersion;
2670 			unsigned char siteCount;
2671 
2672 			printf("    FRU_CARRIER_INFO\n");
2673 
2674 			extVersion = fru_data[offset++];
2675 			siteCount  = fru_data[offset++];
2676 
2677 			printf("      AMC.0 extension version: R%d.%d\n",
2678 					(extVersion >> 0)& 0x0F,
2679 					(extVersion >> 4)& 0x0F );
2680 			printf("      Carrier Sie Number Cnt: %d\n", siteCount);
2681 
2682 			for (i = 0 ; i < siteCount; i++ ){
2683 				printf("       Site ID: %i \n", fru_data[offset++]);
2684 			}
2685 			printf("\n");
2686 		}
2687 		break;
2688 		case FRU_PICMG_CLK_CARRIER_P2P:
2689 		{
2690 			unsigned char desc_count;
2691 			int i,j;
2692 
2693 			printf("    FRU_PICMG_CLK_CARRIER_P2P\n");
2694 
2695 			desc_count = fru_data[offset++];
2696 
2697 			for(i=0; i<desc_count; i++){
2698 				unsigned char resource_id;
2699 				unsigned char channel_count;
2700 
2701 				resource_id   = fru_data[offset++];
2702 				channel_count = fru_data[offset++];
2703 
2704 				printf("\n");
2705 				printf("      Clock Resource ID: 0x%02x  Type: ", resource_id);
2706 				if((resource_id & 0xC0)>>6 == 0) {printf("On-Carrier-Device\n");}
2707 				else if((resource_id & 0xC0)>>6 == 1) {printf("AMC slot\n");}
2708 				else if((resource_id & 0xC0)>>6 == 2) {printf("Backplane\n");}
2709 				else{ printf("reserved\n");}
2710 				printf("      Channel Count: 0x%02x\n", channel_count);
2711 
2712 				for(j=0; j<channel_count; j++){
2713 					unsigned char loc_channel, rem_channel, rem_resource;
2714 
2715 					loc_channel  = fru_data[offset++];
2716 					rem_channel  = fru_data[offset++];
2717 					rem_resource = fru_data[offset++];
2718 
2719 					printf("        CLK-ID: 0x%02x    ->", loc_channel);
2720 					printf(" remote CLKID: 0x%02x   ", rem_channel);
2721 					if((rem_resource & 0xC0)>>6 == 0) {printf("[ Carrier-Dev");}
2722 					else if((rem_resource & 0xC0)>>6 == 1) {printf("[ AMC slot   ");}
2723 					else if((rem_resource & 0xC0)>>6 == 2) {printf("[ Backplane  ");}
2724 					else{ printf("reserved         ");}
2725 					printf(" 0x%02x ]\n", rem_resource&0xF);
2726 				}
2727 			}
2728 			printf("\n");
2729 		}
2730 		break;
2731 		case FRU_PICMG_CLK_CONFIG:
2732 		{
2733 			unsigned char resource_id, descr_count;
2734 			int i,j;
2735 
2736 			printf("    FRU_PICMG_CLK_CONFIG\n");
2737 
2738 			resource_id = fru_data[offset++];
2739 			descr_count = fru_data[offset++];
2740 
2741 			printf("\n");
2742 			printf("      Clock Resource ID: 0x%02x\n", resource_id);
2743 			printf("      Descr. Count:      0x%02x\n", descr_count);
2744 
2745 			for(i=0; i<descr_count; i++){
2746 				unsigned char channel_id, control;
2747 				unsigned char indirect_cnt, direct_cnt;
2748 
2749 				channel_id = fru_data[offset++];
2750 				control    = fru_data[offset++];
2751 				printf("        CLK-ID: 0x%02x  -  ", channel_id);
2752 				printf("CTRL 0x%02x [ %12s ]\n",
2753 									control,
2754 									((control&0x1)==0)?"Carrier IPMC":"Application");
2755 
2756 				indirect_cnt = fru_data[offset++];
2757 				direct_cnt   = fru_data[offset++];
2758 				printf("         Cnt: Indirect 0x%02x  /  Direct 0x%02x\n",
2759 						indirect_cnt,
2760 						direct_cnt);
2761 
2762 				/* indirect desc */
2763 				for(j=0; j<indirect_cnt; j++){
2764 					unsigned char feature;
2765 					unsigned char dep_chn_id;
2766 
2767 					feature    = fru_data[offset++];
2768 					dep_chn_id = fru_data[offset++];
2769 
2770 					printf("          Feature: 0x%02x [%8s] - ", feature, (feature&0x1)==1?"Source":"Receiver");
2771 					printf("          Dep. CLK-ID: 0x%02x\n", dep_chn_id);
2772 				}
2773 
2774 				/* direct desc */
2775 				for(j=0; j<direct_cnt; j++){
2776 					unsigned char feature, family, accuracy;
2777 					unsigned int freq, min_freq, max_freq;
2778 
2779 					feature  = fru_data[offset++];
2780 					family   = fru_data[offset++];
2781 					accuracy = fru_data[offset++];
2782 					freq     = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 )
2783 								| (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24);
2784 					offset += 4;
2785 					min_freq = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 )
2786 								| (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24);
2787 					offset += 4;
2788 					max_freq = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 )
2789 								| (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24);
2790 					offset += 4;
2791 
2792 					printf("          - Feature: 0x%02x  - PLL: %x / Asym: %s\n",
2793 							feature,
2794 							(feature > 1) & 1,
2795 							(feature&1)?"Source":"Receiver");
2796 					printf("            Family:  0x%02x  - AccLVL: 0x%02x\n", family, accuracy);
2797 					printf("            FRQ: %-9ld - min: %-9ld - max: %-9ld\n",
2798 							freq, min_freq, max_freq);
2799 				}
2800 				printf("\n");
2801 			}
2802 			printf("\n");
2803 		}
2804 		break;
2805 
2806 		case FRU_UTCA_FRU_INFO_TABLE:
2807 		case FRU_UTCA_CARRIER_MNG_IP:
2808 		case FRU_UTCA_CARRIER_INFO:
2809 		case FRU_UTCA_CARRIER_LOCATION:
2810 		case FRU_UTCA_SHMC_IP_LINK:
2811 		case FRU_UTCA_POWER_POLICY:
2812 		case FRU_UTCA_ACTIVATION:
2813 		case FRU_UTCA_PM_CAPABILTY:
2814 		case FRU_UTCA_FAN_GEOGRAPHY:
2815 		case FRU_UTCA_CLOCK_MAPPING:
2816 		case FRU_UTCA_MSG_BRIDGE_POLICY:
2817 		case FRU_UTCA_OEM_MODULE_DESC:
2818 			printf("    Not implemented yet. uTCA specific record found!!\n");
2819 			printf("     - Record ID: 0x%02x\n", h->record_id);
2820 		break;
2821 
2822 		default:
2823 			printf("    Unknown OEM Extension Record ID: %x\n", h->record_id);
2824 		break;
2825 
2826 	}
2827 }
2828 
2829 
2830 /* __ipmi_fru_print  -  Do actual work to print a FRU by its ID
2831 *
2832 * @intf:   ipmi interface
2833 * @id:     fru id
2834 *
2835 * returns -1 on error
2836 * returns 0 if successful
2837 * returns 1 if device not present
2838 */
2839 static int
2840 __ipmi_fru_print(struct ipmi_intf * intf, uint8_t id)
2841 {
2842 	struct ipmi_rs * rsp;
2843 	struct ipmi_rq req;
2844 	struct fru_info fru;
2845 	struct fru_header header;
2846 	uint8_t msg_data[4];
2847 
2848 	memset(&fru, 0, sizeof(struct fru_info));
2849 	memset(&header, 0, sizeof(struct fru_header));
2850 
2851 	/*
2852 	* get info about this FRU
2853 	*/
2854 	memset(msg_data, 0, 4);
2855 	msg_data[0] = id;
2856 
2857 	memset(&req, 0, sizeof(req));
2858 	req.msg.netfn = IPMI_NETFN_STORAGE;
2859 	req.msg.cmd = GET_FRU_INFO;
2860 	req.msg.data = msg_data;
2861 	req.msg.data_len = 1;
2862 
2863 	rsp = intf->sendrecv(intf, &req);
2864 	if (rsp == NULL) {
2865 		printf(" Device not present (No Response)\n");
2866 		return -1;
2867 	}
2868 	if (rsp->ccode > 0) {
2869 		printf(" Device not present (%s)\n",
2870 			val2str(rsp->ccode, completion_code_vals));
2871 		return -1;
2872 	}
2873 
2874 	memset(&fru, 0, sizeof(fru));
2875 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
2876 	fru.access = rsp->data[2] & 0x1;
2877 
2878 	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
2879 		fru.size, fru.access ? "words" : "bytes");
2880 
2881 	if (fru.size < 1) {
2882 		lprintf(LOG_ERR, " Invalid FRU size %d", fru.size);
2883 		return -1;
2884 	}
2885 
2886 	/*
2887 	* retrieve the FRU header
2888 	*/
2889 	msg_data[0] = id;
2890 	msg_data[1] = 0;
2891 	msg_data[2] = 0;
2892 	msg_data[3] = 8;
2893 
2894 	memset(&req, 0, sizeof(req));
2895 	req.msg.netfn = IPMI_NETFN_STORAGE;
2896 	req.msg.cmd = GET_FRU_DATA;
2897 	req.msg.data = msg_data;
2898 	req.msg.data_len = 4;
2899 
2900 	rsp = intf->sendrecv(intf, &req);
2901 	if (rsp == NULL) {
2902 		printf(" Device not present (No Response)\n");
2903 		return 1;
2904 	}
2905 	if (rsp->ccode > 0) {
2906 		printf(" Device not present (%s)\n",
2907 				val2str(rsp->ccode, completion_code_vals));
2908 		return 1;
2909 	}
2910 
2911 	if (verbose > 1)
2912 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
2913 
2914 	memcpy(&header, rsp->data + 1, 8);
2915 
2916 	if (header.version != 1) {
2917 		lprintf(LOG_ERR, " Unknown FRU header version 0x%02x",
2918 			header.version);
2919 		return -1;
2920 	}
2921 
2922 	/* offsets need converted to bytes
2923 	* but that conversion is not done to the structure
2924 	* because we may end up with offset > 255
2925 	* which would overflow our 1-byte offset field */
2926 
2927 	lprintf(LOG_DEBUG, "fru.header.version:         0x%x",
2928 		header.version);
2929 	lprintf(LOG_DEBUG, "fru.header.offset.internal: 0x%x",
2930 		header.offset.internal * 8);
2931 	lprintf(LOG_DEBUG, "fru.header.offset.chassis:  0x%x",
2932 		header.offset.chassis * 8);
2933 	lprintf(LOG_DEBUG, "fru.header.offset.board:    0x%x",
2934 		header.offset.board * 8);
2935 	lprintf(LOG_DEBUG, "fru.header.offset.product:  0x%x",
2936 		header.offset.product * 8);
2937 	lprintf(LOG_DEBUG, "fru.header.offset.multi:    0x%x",
2938 		header.offset.multi * 8);
2939 
2940 	/*
2941 	* rather than reading the entire part
2942 	* only read the areas we'll format
2943 	*/
2944 	/* chassis area */
2945 	if ((header.offset.chassis*8) >= sizeof(struct fru_header))
2946 		fru_area_print_chassis(intf, &fru, id, header.offset.chassis*8);
2947 
2948 	/* board area */
2949 	if ((header.offset.board*8) >= sizeof(struct fru_header))
2950 		fru_area_print_board(intf, &fru, id, header.offset.board*8);
2951 
2952 	/* product area */
2953 	if ((header.offset.product*8) >= sizeof(struct fru_header))
2954 		fru_area_print_product(intf, &fru, id, header.offset.product*8);
2955 
2956 	/* multirecord area */
2957 	if( verbose==0 ) /* scipp parsing multirecord */
2958 		return 0;
2959 
2960 	if ((header.offset.multi*8) >= sizeof(struct fru_header))
2961 		fru_area_print_multirec(intf, &fru, id, header.offset.multi*8);
2962 
2963 	return 0;
2964 }
2965 
2966 /* ipmi_fru_print  -  Print a FRU from its SDR locator record
2967 *
2968 * @intf:   ipmi interface
2969 * @fru: SDR FRU Locator Record
2970 *
2971 * returns -1 on error
2972 */
2973 int
2974 ipmi_fru_print(struct ipmi_intf * intf, struct sdr_record_fru_locator * fru)
2975 {
2976 	char desc[17];
2977 	uint8_t  bridged_request = 0;
2978 	uint32_t save_addr;
2979 	uint32_t save_channel;
2980 	int rc = 0;
2981 
2982 	if (fru == NULL)
2983 		return __ipmi_fru_print(intf, 0);
2984 
2985 	/* Logical FRU Device
2986 	*  dev_type == 0x10
2987 	*  modifier
2988 	*   0x00 = IPMI FRU Inventory
2989 	*   0x01 = DIMM Memory ID
2990 	*   0x02 = IPMI FRU Inventory
2991 	*   0x03 = System Processor FRU
2992 	*   0xff = unspecified
2993 	*
2994 	* EEPROM 24C01 or equivalent
2995 	*  dev_type >= 0x08 && dev_type <= 0x0f
2996 	*  modifier
2997 	*   0x00 = unspecified
2998 	*   0x01 = DIMM Memory ID
2999 	*   0x02 = IPMI FRU Inventory
3000 	*   0x03 = System Processor Cartridge
3001 	*/
3002 	if (fru->dev_type != 0x10 &&
3003 		(fru->dev_type_modifier != 0x02 ||
3004 		fru->dev_type < 0x08 || fru->dev_type > 0x0f))
3005 		return -1;
3006 
3007 	if (fru->dev_slave_addr == IPMI_BMC_SLAVE_ADDR &&
3008 		fru->device_id == 0)
3009 		return 0;
3010 
3011 	memset(desc, 0, sizeof(desc));
3012 	memcpy(desc, fru->id_string, fru->id_code & 0x01f);
3013 	desc[fru->id_code & 0x01f] = 0;
3014 	printf("FRU Device Description : %s (ID %d)\n", desc, fru->device_id);
3015 
3016 	switch (fru->dev_type_modifier) {
3017 	case 0x00:
3018 	case 0x02:
3019 		if (BRIDGE_TO_SENSOR(intf, fru->dev_slave_addr,
3020 					   fru->channel_num)) {
3021 			bridged_request = 1;
3022 			save_addr = intf->target_addr;
3023 			intf->target_addr = fru->dev_slave_addr;
3024 			save_channel = intf->target_channel;
3025 			intf->target_channel = fru->channel_num;
3026 		}
3027 		/* print FRU */
3028 		rc = __ipmi_fru_print(intf, fru->device_id);
3029 		if (bridged_request) {
3030 			intf->target_addr = save_addr;
3031 			intf->target_channel = save_channel;
3032 		}
3033 		break;
3034 	case 0x01:
3035 		rc = ipmi_spd_print_fru(intf, fru->device_id);
3036 		break;
3037 	default:
3038 		if (verbose)
3039 			printf(" Unsupported device 0x%02x "
3040 					"type 0x%02x with modifier 0x%02x\n",
3041 					fru->device_id, fru->dev_type,
3042 					fru->dev_type_modifier);
3043 		else
3044 			printf(" Unsupported device\n");
3045 	}
3046 	printf("\n");
3047 
3048 	return rc;
3049 }
3050 
3051 /* ipmi_fru_print_all  -  Print builtin FRU + SDR FRU Locator records
3052 *
3053 * @intf:   ipmi interface
3054 *
3055 * returns -1 on error
3056 */
3057 static int
3058 ipmi_fru_print_all(struct ipmi_intf * intf)
3059 {
3060 	struct ipmi_sdr_iterator * itr;
3061 	struct sdr_get_rs * header;
3062 	struct sdr_record_fru_locator * fru;
3063 	int rc;
3064 	struct ipmi_rs * rsp;
3065 	struct ipmi_rq req;
3066 	struct ipm_devid_rsp *devid;
3067 	struct sdr_record_mc_locator * mc;
3068 	uint32_t save_addr;
3069 
3070 	printf("FRU Device Description : Builtin FRU Device (ID 0)\n");
3071 	/* TODO: Figure out if FRU device 0 may show up in SDR records. */
3072 
3073 	/* Do a Get Device ID command to determine device support */
3074 	memset (&req, 0, sizeof(req));
3075 	req.msg.netfn = IPMI_NETFN_APP;
3076 	req.msg.cmd = BMC_GET_DEVICE_ID;
3077 	req.msg.data_len = 0;
3078 
3079 	rsp = intf->sendrecv(intf, &req);
3080 	if (rsp == NULL) {
3081 		lprintf(LOG_ERR, "Get Device ID command failed");
3082 		return -1;
3083 	}
3084 	if (rsp->ccode > 0) {
3085 		lprintf(LOG_ERR, "Get Device ID command failed: %s",
3086 			val2str(rsp->ccode, completion_code_vals));
3087 		return -1;
3088 	}
3089 
3090 	devid = (struct ipm_devid_rsp *) rsp->data;
3091 
3092 	/* Check the FRU inventory device bit to decide whether various */
3093 	/* FRU commands can be issued to FRU device #0 LUN 0		*/
3094 
3095 	if (devid->adtl_device_support & 0x08) {	/* FRU Inventory Device bit? */
3096 		rc = ipmi_fru_print(intf, NULL);
3097 		printf("\n");
3098 	}
3099 
3100 	if ((itr = ipmi_sdr_start(intf, 0)) == NULL)
3101 		return -1;
3102 
3103 	/* Walk the SDRs looking for FRU Devices and Management Controller Devices. */
3104 	/* For FRU devices, print the FRU from the SDR locator record.		    */
3105 	/* For MC devices, issue FRU commands to the satellite controller to print  */
3106 	/* FRU data.								    */
3107 	while ((header = ipmi_sdr_get_next_header(intf, itr)) != NULL)
3108 	{
3109 		if (header->type == SDR_RECORD_TYPE_MC_DEVICE_LOCATOR ) {
3110 			/* Check the capabilities of the Management Controller Device */
3111 			mc = (struct sdr_record_mc_locator *)
3112 				ipmi_sdr_get_record(intf, header, itr);
3113 			/* Does this MC device support FRU inventory device? */
3114 			if (mc && (mc->dev_support & 0x08) && /* FRU inventory device? */
3115 				intf->target_addr != mc->dev_slave_addr) {
3116 				/* Yes. Prepare to issue FRU commands to FRU device #0 LUN 0  */
3117 				/* using the slave address specified in the MC record.	      */
3118 
3119 				/* save current target address */
3120 				save_addr = intf->target_addr;
3121 
3122 				/* set new target address to satellite controller */
3123 				intf->target_addr = mc->dev_slave_addr;
3124 
3125 				printf("FRU Device Description : %-16s\n", mc->id_string);
3126 
3127 				/* print the FRU by issuing FRU commands to the satellite     */
3128 				/* controller.						      */
3129 				rc = __ipmi_fru_print(intf, 0);
3130 
3131 				printf("\n");
3132 
3133 				/* restore previous target */
3134 				intf->target_addr = save_addr;
3135 			}
3136 
3137 			if (mc) {
3138 				free(mc);
3139 				mc = NULL;
3140 			}
3141 
3142 			continue;
3143 		}
3144 
3145 		if (header->type != SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR)
3146 			continue;
3147 
3148 		/* Print the FRU from the SDR locator record. */
3149 		fru = (struct sdr_record_fru_locator *)
3150 			ipmi_sdr_get_record(intf, header, itr);
3151 		if (fru == NULL || !fru->logical) {
3152 			if (fru) {
3153 				free(fru);
3154 				fru = NULL;
3155 			}
3156 			continue;
3157 		}
3158 		rc = ipmi_fru_print(intf, fru);
3159 		free(fru);
3160 		fru = NULL;
3161 	}
3162 
3163 	ipmi_sdr_end(intf, itr);
3164 
3165 	return rc;
3166 }
3167 
3168 /* ipmi_fru_read_help() - print help text for 'read'
3169  *
3170  * returns void
3171  */
3172 void
3173 ipmi_fru_read_help()
3174 {
3175 	lprintf(LOG_NOTICE, "fru read <fru id> <fru file>");
3176 	lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified.");
3177 	lprintf(LOG_NOTICE, "Example: ipmitool fru read 0 /root/fru.bin");
3178 } /* ipmi_fru_read_help() */
3179 
3180 static void
3181 ipmi_fru_read_to_bin(struct ipmi_intf * intf,
3182 			char * pFileName,
3183 			uint8_t fruId)
3184 {
3185 	struct ipmi_rs * rsp;
3186 	struct ipmi_rq req;
3187 	struct fru_info fru;
3188 	uint8_t msg_data[4];
3189 	uint8_t * pFruBuf;
3190 
3191 	msg_data[0] = fruId;
3192 
3193 	memset(&req, 0, sizeof(req));
3194 	req.msg.netfn = IPMI_NETFN_STORAGE;
3195 	req.msg.cmd = GET_FRU_INFO;
3196 	req.msg.data = msg_data;
3197 	req.msg.data_len = 1;
3198 
3199 	rsp = intf->sendrecv(intf, &req);
3200 	if (!rsp)
3201 		return;
3202 
3203 	if (rsp->ccode > 0) {
3204 		if (rsp->ccode == 0xc3)
3205 			printf ("  Timeout accessing FRU info. (Device not present?)\n");
3206 		return;
3207 	}
3208 
3209 	memset(&fru, 0, sizeof(fru));
3210 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
3211 	fru.access = rsp->data[2] & 0x1;
3212 
3213 	if (verbose) {
3214 		printf("Fru Size   = %d bytes\n",fru.size);
3215 		printf("Fru Access = %xh\n", fru.access);
3216 	}
3217 
3218 	pFruBuf = malloc(fru.size);
3219 	if (pFruBuf != NULL) {
3220 		printf("Fru Size         : %d bytes\n",fru.size);
3221 		read_fru_area(intf, &fru, fruId, 0, fru.size, pFruBuf);
3222 	} else {
3223 		lprintf(LOG_ERR, "Cannot allocate %d bytes\n", fru.size);
3224 		return;
3225 	}
3226 
3227 	if(pFruBuf != NULL)
3228 	{
3229 		FILE * pFile;
3230 		pFile = fopen(pFileName,"wb");
3231 		if (pFile) {
3232 			fwrite(pFruBuf, fru.size, 1, pFile);
3233 			printf("Done\n");
3234 		} else {
3235 			lprintf(LOG_ERR, "Error opening file %s\n", pFileName);
3236 			free(pFruBuf);
3237 			pFruBuf = NULL;
3238 			return;
3239 		}
3240 		fclose(pFile);
3241 	}
3242 	free(pFruBuf);
3243 	pFruBuf = NULL;
3244 }
3245 
3246 static void
3247 ipmi_fru_write_from_bin(struct ipmi_intf * intf,
3248 			char * pFileName,
3249 			uint8_t fruId)
3250 {
3251 	struct ipmi_rs *rsp;
3252 	struct ipmi_rq req;
3253 	struct fru_info fru;
3254 	uint8_t msg_data[4];
3255 	uint8_t *pFruBuf;
3256 	uint16_t len = 0;
3257 	FILE *pFile;
3258 
3259 	msg_data[0] = fruId;
3260 
3261 	memset(&req, 0, sizeof (req));
3262 	req.msg.netfn = IPMI_NETFN_STORAGE;
3263 	req.msg.cmd = GET_FRU_INFO;
3264 	req.msg.data = msg_data;
3265 	req.msg.data_len = 1;
3266 
3267 	rsp = intf->sendrecv(intf, &req);
3268 	if (!rsp)
3269 		return;
3270 
3271 	if (rsp->ccode) {
3272 		if (rsp->ccode == 0xc3)
3273 			printf("  Timeout accessing FRU info. (Device not present?)\n");
3274 		return;
3275 	}
3276 
3277 	memset(&fru, 0, sizeof(fru));
3278 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
3279 	fru.access = rsp->data[2] & 0x1;
3280 
3281 	if (verbose) {
3282 		printf("Fru Size   = %d bytes\n", fru.size);
3283 		printf("Fru Access = %xh\n", fru.access);
3284 	}
3285 
3286 	pFruBuf = malloc(fru.size);
3287 	if (pFruBuf == NULL) {
3288 		lprintf(LOG_ERR, "Cannot allocate %d bytes\n", fru.size);
3289 		return;
3290 	}
3291 
3292 		pFile = fopen(pFileName, "rb");
3293 		if (pFile != NULL) {
3294 			len = fread(pFruBuf, 1, fru.size, pFile);
3295 			printf("Fru Size         : %d bytes\n", fru.size);
3296 			printf("Size to Write    : %d bytes\n", len);
3297 			fclose(pFile);
3298 		} else {
3299 		lprintf(LOG_ERR, "Error opening file %s\n", pFileName);
3300 		}
3301 
3302 		if (len != 0) {
3303 			write_fru_area(intf, &fru, fruId,0, 0, len, pFruBuf);
3304 			lprintf(LOG_INFO,"Done");
3305 		}
3306 
3307 	free(pFruBuf);
3308 	pFruBuf = NULL;
3309 }
3310 
3311 /* ipmi_fru_write_help() - print help text for 'write'
3312  *
3313  * retruns void
3314  */
3315 void
3316 ipmi_fru_write_help()
3317 {
3318 	lprintf(LOG_NOTICE, "fru write <fru id> <fru file>");
3319 	lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified.");
3320 	lprintf(LOG_NOTICE, "Example: ipmitool fru write 0 /root/fru.bin");
3321 } /* ipmi_fru_write_help() */
3322 
3323 /* ipmi_fru_edit_help - print help text for 'fru edit' command
3324  *
3325  * returns void
3326  */
3327 void
3328 ipmi_fru_edit_help()
3329 {
3330 	lprintf(LOG_NOTICE,
3331 			"fru edit <fruid> field <section> <index> <string> - edit FRU string");
3332 	lprintf(LOG_NOTICE,
3333 			"fru edit <fruid> oem iana <record> <format> <args> - limited OEM support");
3334 } /* ipmi_fru_edit_help() */
3335 
3336 /* ipmi_fru_edit_multirec  -  Query new values to replace original FRU content
3337 *
3338 * @intf:   interface to use
3339 * @id:  FRU id to work on
3340 *
3341 * returns: nothing
3342 */
3343 /* Work in progress, copy paste most of the stuff for other functions in this
3344 	file ... not elegant yet */
3345 static int
3346 ipmi_fru_edit_multirec(struct ipmi_intf * intf, uint8_t id ,
3347 												int argc, char ** argv)
3348 {
3349 
3350 	struct ipmi_rs * rsp;
3351 	struct ipmi_rq req;
3352 	struct fru_info fru;
3353 	struct fru_header header;
3354 	uint8_t msg_data[4];
3355 
3356 	uint16_t retStatus = 0;
3357 	uint32_t offFruMultiRec;
3358 	uint32_t fruMultiRecSize = 0;
3359 	struct fru_info fruInfo;
3360 	retStatus = ipmi_fru_get_multirec_location_from_fru(intf, id, &fruInfo,
3361 								&offFruMultiRec,
3362 								&fruMultiRecSize);
3363 
3364 
3365 	lprintf(LOG_DEBUG, "FRU Size        : %lu\n", fruMultiRecSize);
3366 	lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec);
3367 
3368 	{
3369 
3370 
3371 	memset(&fru, 0, sizeof(struct fru_info));
3372 	memset(&header, 0, sizeof(struct fru_header));
3373 
3374 	/*
3375 	* get info about this FRU
3376 	*/
3377 	memset(msg_data, 0, 4);
3378 	msg_data[0] = id;
3379 
3380 	memset(&req, 0, sizeof(req));
3381 	req.msg.netfn = IPMI_NETFN_STORAGE;
3382 	req.msg.cmd = GET_FRU_INFO;
3383 	req.msg.data = msg_data;
3384 	req.msg.data_len = 1;
3385 
3386 	rsp = intf->sendrecv(intf, &req);
3387 	if (rsp == NULL) {
3388 		printf(" Device not present (No Response)\n");
3389 		return -1;
3390 	}
3391 	if (rsp->ccode > 0) {
3392 		printf(" Device not present (%s)\n",
3393 			val2str(rsp->ccode, completion_code_vals));
3394 		return -1;
3395 	}
3396 
3397 	memset(&fru, 0, sizeof(fru));
3398 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
3399 	fru.access = rsp->data[2] & 0x1;
3400 
3401 	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
3402 		fru.size, fru.access ? "words" : "bytes");
3403 
3404 	if (fru.size < 1) {
3405 		lprintf(LOG_ERR, " Invalid FRU size %d", fru.size);
3406 		return -1;
3407 	}
3408 	}
3409 
3410 	{
3411 		uint8_t * fru_data;
3412 		uint32_t fru_len, i;
3413 		uint32_t offset= offFruMultiRec;
3414 		struct fru_multirec_header * h;
3415 		uint32_t last_off, len;
3416 		uint8_t error=0;
3417 
3418 		i = last_off = offset;
3419 		fru_len = 0;
3420 
3421 		memset(&fru, 0, sizeof(fru));
3422 		fru_data = malloc(fru.size + 1);
3423 		if (fru_data == NULL) {
3424 			lprintf(LOG_ERR, " Out of memory!");
3425 			return -1;
3426 		}
3427 		memset(fru_data, 0, fru.size + 1);
3428 
3429 		do {
3430 			h = (struct fru_multirec_header *) (fru_data + i);
3431 
3432 			/* read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time */
3433 			if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len)))
3434 			{
3435 				len = fru.size - last_off;
3436 				if (len > FRU_MULTIREC_CHUNK_SIZE)
3437 					len = FRU_MULTIREC_CHUNK_SIZE;
3438 
3439 				if (read_fru_area(intf, &fru, id, last_off, len, fru_data) < 0)
3440 					break;
3441 
3442 				last_off += len;
3443 			}
3444 			if( h->type ==  FRU_RECORD_TYPE_OEM_EXTENSION ){
3445 
3446 				struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *)
3447 										&fru_data[i + sizeof(struct fru_multirec_header)];
3448 				uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16;
3449 
3450 				uint32_t suppliedIana = 0 ;
3451 				/* Now makes sure this is really PICMG record */
3452 
3453 				/* Default to PICMG for backward compatibility */
3454 				if( argc <=2 ) {
3455 					suppliedIana =  IPMI_OEM_PICMG;
3456 				}  else {
3457 					if( !strncmp( argv[2] , "oem" , 3 )) {
3458 						/* Expect IANA number next */
3459 						if( argc <= 3 ) {
3460 							lprintf(LOG_ERR, "oem iana <record> <format> [<args>]");
3461 							error = 1;
3462 						} else {
3463 							if (str2uint(argv[3], &suppliedIana) == 0) {
3464 								lprintf(LOG_DEBUG,
3465 										"using iana: %d",
3466 										suppliedIana);
3467 							} else {
3468 								lprintf(LOG_ERR,
3469 										"Given IANA '%s' is invalid.",
3470 										argv[3]);
3471 								error = 1;
3472 							}
3473 						}
3474 					}
3475 				}
3476 
3477 				if( suppliedIana == iana ) {
3478 					lprintf(LOG_DEBUG, "Matching record found" );
3479 
3480 					if( iana == IPMI_OEM_PICMG ){
3481 						if( ipmi_fru_picmg_ext_edit(fru_data,
3482 						i + sizeof(struct fru_multirec_header),
3483 						h->len, h, oh )){
3484 							/* The fru changed */
3485 							write_fru_area(intf,&fru,id, i,i,
3486 						h->len+ sizeof(struct fru_multirec_header), fru_data);
3487 						}
3488 					}
3489 					else if( iana == IPMI_OEM_KONTRON ) {
3490 						if( ipmi_fru_oemkontron_edit( argc,argv,fru_data,
3491 						i + sizeof(struct fru_multirec_header),
3492 						h->len, h, oh )){
3493 							/* The fru changed */
3494 							write_fru_area(intf,&fru,id, i,i,
3495 						h->len+ sizeof(struct fru_multirec_header), fru_data);
3496 						}
3497 					}
3498 					/* FIXME: Add OEM record support here */
3499 					else{
3500 						printf("  OEM IANA (%s) Record not support in this mode\n",
3501 															val2str( iana,  ipmi_oem_info));
3502 						error = 1;
3503 					}
3504 				}
3505 			}
3506 			i += h->len + sizeof (struct fru_multirec_header);
3507 		} while (!(h->format & 0x80) && (error != 1));
3508 
3509 		free(fru_data);
3510 		fru_data = NULL;
3511 	}
3512 	return 0;
3513 }
3514 
3515 /* ipmi_fru_get_help - print help text for 'fru get'
3516  *
3517  * returns void
3518  */
3519 void
3520 ipmi_fru_get_help()
3521 {
3522 	lprintf(LOG_NOTICE,
3523 			"fru get <fruid> oem iana <record> <format> <args> - limited OEM support");
3524 } /* ipmi_fru_get_help() */
3525 
3526 void
3527 ipmi_fru_internaluse_help()
3528 {
3529 	lprintf(LOG_NOTICE,
3530 			"fru internaluse <fru id> info             - get internal use area size");
3531 	lprintf(LOG_NOTICE,
3532 			"fru internaluse <fru id> print            - print internal use area in hex");
3533 	lprintf(LOG_NOTICE,
3534 			"fru internaluse <fru id> read  <fru file> - read internal use area to file");
3535 	lprintf(LOG_NOTICE,
3536 			"fru internaluse <fru id> write <fru file> - write internal use area from file");
3537 } /* void ipmi_fru_internaluse_help() */
3538 
3539 /* ipmi_fru_get_multirec   -  Query new values to replace original FRU content
3540 *
3541 * @intf:   interface to use
3542 * @id:  FRU id to work on
3543 *
3544 * returns: nothing
3545 */
3546 /* Work in progress, copy paste most of the stuff for other functions in this
3547 	file ... not elegant yet */
3548 static int
3549 ipmi_fru_get_multirec(struct ipmi_intf * intf, uint8_t id ,
3550 												int argc, char ** argv)
3551 {
3552 
3553 	struct ipmi_rs * rsp;
3554 	struct ipmi_rq req;
3555 	struct fru_info fru;
3556 	struct fru_header header;
3557 	uint8_t msg_data[4];
3558 
3559 	uint16_t retStatus = 0;
3560 	uint32_t offFruMultiRec;
3561 	uint32_t fruMultiRecSize = 0;
3562 	struct fru_info fruInfo;
3563 	retStatus = ipmi_fru_get_multirec_location_from_fru(intf, id, &fruInfo,
3564 								&offFruMultiRec,
3565 								&fruMultiRecSize);
3566 
3567 
3568 	lprintf(LOG_DEBUG, "FRU Size        : %lu\n", fruMultiRecSize);
3569 	lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec);
3570 
3571 	{
3572 
3573 
3574 	memset(&fru, 0, sizeof(struct fru_info));
3575 	memset(&header, 0, sizeof(struct fru_header));
3576 
3577 	/*
3578 	* get info about this FRU
3579 	*/
3580 	memset(msg_data, 0, 4);
3581 	msg_data[0] = id;
3582 
3583 	memset(&req, 0, sizeof(req));
3584 	req.msg.netfn = IPMI_NETFN_STORAGE;
3585 	req.msg.cmd = GET_FRU_INFO;
3586 	req.msg.data = msg_data;
3587 	req.msg.data_len = 1;
3588 
3589 	rsp = intf->sendrecv(intf, &req);
3590 	if (rsp == NULL) {
3591 		printf(" Device not present (No Response)\n");
3592 		return -1;
3593 	}
3594 	if (rsp->ccode > 0) {
3595 		printf(" Device not present (%s)\n",
3596 			val2str(rsp->ccode, completion_code_vals));
3597 		return -1;
3598 	}
3599 
3600 	memset(&fru, 0, sizeof(fru));
3601 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
3602 	fru.access = rsp->data[2] & 0x1;
3603 
3604 	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
3605 		fru.size, fru.access ? "words" : "bytes");
3606 
3607 	if (fru.size < 1) {
3608 		lprintf(LOG_ERR, " Invalid FRU size %d", fru.size);
3609 		return -1;
3610 	}
3611 	}
3612 
3613 	{
3614 		uint8_t * fru_data;
3615 		uint32_t fru_len, i;
3616 		uint32_t offset= offFruMultiRec;
3617 		struct fru_multirec_header * h;
3618 		uint32_t last_off, len;
3619 		uint8_t error=0;
3620 
3621 		i = last_off = offset;
3622 		fru_len = 0;
3623 
3624 		fru_data = malloc(fru.size + 1);
3625 		if (fru_data == NULL) {
3626 			lprintf(LOG_ERR, " Out of memory!");
3627 			return -1;
3628 		}
3629 		memset(fru_data, 0, fru.size + 1);
3630 
3631 		do {
3632 			h = (struct fru_multirec_header *) (fru_data + i);
3633 
3634 			/* read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time */
3635 			if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len)))
3636 			{
3637 				len = fru.size - last_off;
3638 				if (len > FRU_MULTIREC_CHUNK_SIZE)
3639 					len = FRU_MULTIREC_CHUNK_SIZE;
3640 
3641 				if (read_fru_area(intf, &fru, id, last_off, len, fru_data) < 0)
3642 					break;
3643 
3644 				last_off += len;
3645 			}
3646 			if( h->type ==  FRU_RECORD_TYPE_OEM_EXTENSION ){
3647 
3648 				struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *)
3649 										&fru_data[i + sizeof(struct fru_multirec_header)];
3650 				uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16;
3651 
3652 				uint32_t suppliedIana = 0 ;
3653 				/* Now makes sure this is really PICMG record */
3654 				if( !strncmp( argv[2] , "oem" , 3 )) {
3655 					/* Expect IANA number next */
3656 					if( argc <= 3 ) {
3657 						lprintf(LOG_ERR, "oem iana <record> <format>");
3658 						error = 1;
3659 					} else {
3660 						if (str2uint(argv[3], &suppliedIana) == 0) {
3661 							lprintf(LOG_DEBUG,
3662 									"using iana: %d",
3663 									suppliedIana);
3664 						} else {
3665 							lprintf(LOG_ERR,
3666 									"Given IANA '%s' is invalid.",
3667 									argv[3]);
3668 							error = 1;
3669 						}
3670 					}
3671 				}
3672 
3673 				if( suppliedIana == iana ) {
3674 					lprintf(LOG_DEBUG, "Matching record found" );
3675 
3676 				if( iana == IPMI_OEM_KONTRON ) {
3677 						ipmi_fru_oemkontron_get( argc,argv,fru_data,
3678 						i + sizeof(struct fru_multirec_header),
3679 						h->len, h, oh );
3680 					}
3681 					/* FIXME: Add OEM record support here */
3682 					else{
3683 						printf("  OEM IANA (%s) Record not supported in this mode\n",
3684 															val2str( iana,  ipmi_oem_info));
3685 						error = 1;
3686 					}
3687 				}
3688 			}
3689 			i += h->len + sizeof (struct fru_multirec_header);
3690 		} while (!(h->format & 0x80) && (error != 1));
3691 
3692 		free(fru_data);
3693 		fru_data = NULL;
3694 	}
3695 	return 0;
3696 }
3697 
3698 static int
3699 ipmi_fru_upg_ekeying(struct ipmi_intf * intf,
3700 			char * pFileName,
3701 			uint8_t fruId)
3702 {
3703 	struct fru_info fruInfo;
3704 	uint8_t *buf = NULL;
3705 	uint32_t offFruMultiRec = 0;
3706 	uint32_t fruMultiRecSize = 0;
3707 	uint32_t offFileMultiRec = 0;
3708 	uint32_t fileMultiRecSize = 0;
3709 	if (pFileName == NULL) {
3710 		lprintf(LOG_ERR, "File expected, but none given.");
3711 		return (-1);
3712 	}
3713 	if (ipmi_fru_get_multirec_location_from_fru(intf, fruId, &fruInfo,
3714 							&offFruMultiRec, &fruMultiRecSize) != 0) {
3715 		lprintf(LOG_ERR, "Failed to get multirec location from FRU.");
3716 		return (-1);
3717 	}
3718 	lprintf(LOG_DEBUG, "FRU Size        : %lu\n", fruMultiRecSize);
3719 	lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec);
3720 	if (ipmi_fru_get_multirec_size_from_file(pFileName, &fileMultiRecSize,
3721 				&offFileMultiRec) != 0) {
3722 		lprintf(LOG_ERR, "Failed to get multirec size from file '%s'.", pFileName);
3723 		return (-1);
3724 	}
3725 	buf = malloc(fileMultiRecSize);
3726 	if (buf == NULL) {
3727 		lprintf(LOG_ERR, "ipmitool: malloc failure");
3728 		return (-1);
3729 	}
3730 	if (ipmi_fru_get_multirec_from_file(pFileName, buf, fileMultiRecSize,
3731 				offFileMultiRec) != 0) {
3732 		lprintf(LOG_ERR, "Failed to get multirec from file '%s'.", pFileName);
3733 		if (buf != NULL) {
3734 			free(buf);
3735 			buf = NULL;
3736 		}
3737 		return (-1);
3738 	}
3739 	if (ipmi_fru_get_adjust_size_from_buffer(buf, &fileMultiRecSize) != 0) {
3740 		lprintf(LOG_ERR, "Failed to adjust size from buffer.");
3741 		if (buf != NULL) {
3742 			free(buf);
3743 			buf = NULL;
3744 		}
3745 		return (-1);
3746 	}
3747 	if (write_fru_area(intf, &fruInfo, fruId, 0, offFruMultiRec,
3748 				fileMultiRecSize, buf) != 0) {
3749 		lprintf(LOG_ERR, "Failed to write FRU area.");
3750 		if (buf != NULL) {
3751 			free(buf);
3752 			buf = NULL;
3753 		}
3754 		return (-1);
3755 	}
3756 	if (buf != NULL) {
3757 		free(buf);
3758 		buf = NULL;
3759 	}
3760 	lprintf(LOG_INFO, "Done upgrading Ekey.");
3761 	return 0;
3762 }
3763 
3764 /* ipmi_fru_upgekey_help - print help text for 'upgEkey'
3765  *
3766  * returns void
3767  */
3768 void
3769 ipmi_fru_upgekey_help()
3770 {
3771 	lprintf(LOG_NOTICE, "fru upgEkey <fru id> <fru file>");
3772 	lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified.");
3773 	lprintf(LOG_NOTICE, "Example: ipmitool fru upgEkey 0 /root/fru.bin");
3774 } /* ipmi_fru_upgekey_help() */
3775 
3776 static int
3777 ipmi_fru_get_multirec_size_from_file(char * pFileName,
3778 					uint32_t * pSize,
3779 					uint32_t * pOffset)
3780 {
3781 	struct fru_header header;
3782 	FILE * pFile;
3783 	uint8_t len = 0;
3784 	uint32_t end = 0;
3785 	*pSize = 0;
3786 
3787 	pFile = fopen(pFileName,"rb");
3788 	if (pFile) {
3789 		rewind(pFile);
3790 		len = fread(&header, 1, 8, pFile);
3791 		fseek(pFile, 0, SEEK_END);
3792 		end = ftell(pFile);
3793 		fclose(pFile);
3794 	}
3795 
3796 	lprintf(LOG_DEBUG, "File Size = %lu\n", end);
3797 	lprintf(LOG_DEBUG, "Len = %u\n", len);
3798 
3799 	if (len != 8) {
3800 		printf("Error with file %s in getting size\n", pFileName);
3801 		return -1;
3802 	}
3803 
3804 	if (header.version != 0x01) {
3805 		printf ("Unknown FRU header version %02x.\n", header.version);
3806 		return -1;
3807 	}
3808 
3809 	/* Retreive length */
3810 	if (((header.offset.internal * 8) > (header.offset.internal * 8)) &&
3811 		((header.offset.internal * 8) < end))
3812 		end = (header.offset.internal * 8);
3813 
3814 	if (((header.offset.chassis * 8) > (header.offset.chassis * 8)) &&
3815 		((header.offset.chassis * 8) < end))
3816 		end = (header.offset.chassis * 8);
3817 
3818 	if (((header.offset.board * 8) > (header.offset.board * 8)) &&
3819 		((header.offset.board * 8) < end))
3820 		end = (header.offset.board * 8);
3821 
3822 	if (((header.offset.product * 8) > (header.offset.product * 8)) &&
3823 		((header.offset.product * 8) < end))
3824 		end = (header.offset.product * 8);
3825 
3826 	*pSize = end - (header.offset.multi * 8);
3827 	*pOffset = (header.offset.multi * 8);
3828 
3829 	return 0;
3830 }
3831 
3832 int
3833 ipmi_fru_get_adjust_size_from_buffer(uint8_t * fru_data, uint32_t *pSize)
3834 {
3835 	struct fru_multirec_header * head;
3836 	int status = 0;
3837 	uint8_t checksum = 0;
3838 	uint8_t counter = 0;
3839 	uint16_t count = 0;
3840 	do {
3841 		checksum = 0;
3842 		head = (struct fru_multirec_header *) (fru_data + count);
3843 		if (verbose) {
3844 			printf("Adding (");
3845 		}
3846 		for (counter = 0; counter < sizeof(struct fru_multirec_header); counter++) {
3847 			if (verbose) {
3848 				printf(" %02X", *(fru_data + count + counter));
3849 			}
3850 			checksum += *(fru_data + count + counter);
3851 		}
3852 		if (verbose) {
3853 			printf(")");
3854 		}
3855 		if (checksum != 0) {
3856 			lprintf(LOG_ERR, "Bad checksum in Multi Records");
3857 			status = (-1);
3858 			if (verbose) {
3859 				printf("--> FAIL");
3860 			}
3861 		} else if (verbose) {
3862 			printf("--> OK");
3863 		}
3864 		if (verbose > 1 && checksum == 0) {
3865 			for (counter = 0; counter < head->len; counter++) {
3866 				printf(" %02X", *(fru_data + count + counter
3867 							+ sizeof(struct fru_multirec_header)));
3868 			}
3869 		}
3870 		if (verbose) {
3871 			printf("\n");
3872 		}
3873 		count += head->len + sizeof (struct fru_multirec_header);
3874 	} while ((!(head->format & 0x80)) && (status == 0));
3875 
3876 	*pSize = count;
3877 	lprintf(LOG_DEBUG, "Size of multirec: %lu\n", *pSize);
3878 	return status;
3879 }
3880 
3881 static int
3882 ipmi_fru_get_multirec_from_file(char * pFileName, uint8_t * pBufArea,
3883 		uint32_t size, uint32_t offset)
3884 {
3885 	FILE * pFile;
3886 	uint32_t len = 0;
3887 	if (pFileName == NULL) {
3888 		lprintf(LOG_ERR, "Invalid file name given.");
3889 		return (-1);
3890 	}
3891 
3892 	errno = 0;
3893 	pFile = fopen(pFileName, "rb");
3894 	if (!pFile) {
3895 		lprintf(LOG_ERR, "Error opening file '%s': %i -> %s.", pFileName, errno,
3896 				strerror(errno));
3897 		return (-1);
3898 	}
3899 	errno = 0;
3900 	if (fseek(pFile, offset, SEEK_SET) != 0) {
3901 		lprintf(LOG_ERR, "Failed to seek in file '%s': %i -> %s.", pFileName, errno,
3902 				strerror(errno));
3903 		fclose(pFile);
3904 		return (-1);
3905 	}
3906 	len = fread(pBufArea, size, 1, pFile);
3907 	fclose(pFile);
3908 
3909 	if (len != 1) {
3910 		lprintf(LOG_ERR, "Error in file '%s'.", pFileName);
3911 		return (-1);
3912 	}
3913 	return 0;
3914 }
3915 
3916 static int
3917 ipmi_fru_get_multirec_location_from_fru(struct ipmi_intf * intf,
3918 					uint8_t fruId,
3919 												struct fru_info *pFruInfo,
3920 					uint32_t * pRetLocation,
3921 					uint32_t * pRetSize)
3922 {
3923 	struct ipmi_rs * rsp;
3924 	struct ipmi_rq req;
3925 	uint8_t msg_data[4];
3926 	uint32_t end;
3927 	struct fru_header header;
3928 
3929 	*pRetLocation = 0;
3930 
3931 	msg_data[0] = fruId;
3932 
3933 	memset(&req, 0, sizeof(req));
3934 	req.msg.netfn = IPMI_NETFN_STORAGE;
3935 	req.msg.cmd = GET_FRU_INFO;
3936 	req.msg.data = msg_data;
3937 	req.msg.data_len = 1;
3938 
3939 	rsp = intf->sendrecv(intf, &req);
3940 	if (!rsp) {
3941 		if (verbose > 1)
3942 			printf("no response\n");
3943 		return -1;
3944 	}
3945 
3946 	if (rsp->ccode > 0) {
3947 		if (rsp->ccode == 0xc3)
3948 			printf ("  Timeout accessing FRU info. (Device not present?)\n");
3949 		else
3950 			printf ("   CCODE = 0x%02x\n", rsp->ccode);
3951 		return -1;
3952 	}
3953 	pFruInfo->size = (rsp->data[1] << 8) | rsp->data[0];
3954 	pFruInfo->access = rsp->data[2] & 0x1;
3955 
3956 	if (verbose > 1)
3957 		printf("pFruInfo->size = %d bytes (accessed by %s)\n",
3958 				pFruInfo->size, pFruInfo->access ? "words" : "bytes");
3959 
3960 	if (!pFruInfo->size)
3961 		return -1;
3962 
3963 	msg_data[0] = fruId;
3964 	msg_data[1] = 0;
3965 	msg_data[2] = 0;
3966 	msg_data[3] = 8;
3967 
3968 	memset(&req, 0, sizeof(req));
3969 	req.msg.netfn = IPMI_NETFN_STORAGE;
3970 	req.msg.cmd = GET_FRU_DATA;
3971 	req.msg.data = msg_data;
3972 	req.msg.data_len = 4;
3973 
3974 	rsp = intf->sendrecv(intf, &req);
3975 
3976 	if (!rsp)
3977 		return -1;
3978 	if (rsp->ccode > 0) {
3979 		if (rsp->ccode == 0xc3)
3980 			printf ("  Timeout while reading FRU data. (Device not present?)\n");
3981 		return -1;
3982 	}
3983 
3984 	if (verbose > 1)
3985 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
3986 
3987 	memcpy(&header, rsp->data + 1, 8);
3988 
3989 	if (header.version != 0x01) {
3990 		printf ("  Unknown FRU header version %02x.\n", header.version);
3991 		return -1;
3992 	}
3993 
3994 	end = pFruInfo->size;
3995 
3996 	/* Retreive length */
3997 	if (((header.offset.internal * 8) > (header.offset.internal * 8)) &&
3998 		((header.offset.internal * 8) < end))
3999 		end = (header.offset.internal * 8);
4000 
4001 	if (((header.offset.chassis * 8) > (header.offset.chassis * 8)) &&
4002 		((header.offset.chassis * 8) < end))
4003 		end = (header.offset.chassis * 8);
4004 
4005 	if (((header.offset.board * 8) > (header.offset.board * 8)) &&
4006 		((header.offset.board * 8) < end))
4007 		end = (header.offset.board * 8);
4008 
4009 	if (((header.offset.product * 8) > (header.offset.product * 8)) &&
4010 		((header.offset.product * 8) < end))
4011 		end = (header.offset.product * 8);
4012 
4013 	*pRetSize = end;
4014 	*pRetLocation = 8 * header.offset.multi;
4015 
4016 	return 0;
4017 }
4018 
4019 /* ipmi_fru_get_internal_use_offset -  Retreive internal use offset
4020 *
4021 * @intf:   ipmi interface
4022 * @id:     fru id
4023 *
4024 * returns -1 on error
4025 * returns 0 if successful
4026 * returns 1 if device not present
4027 */
4028 static int
4029 ipmi_fru_get_internal_use_info(  struct ipmi_intf * intf,
4030 											uint8_t id,
4031 											struct fru_info * fru,
4032 											uint16_t * size,
4033 											uint16_t * offset)
4034 {
4035 	struct ipmi_rs * rsp;
4036 	struct ipmi_rq req;
4037 	struct fru_header header;
4038 	uint8_t msg_data[4];
4039 
4040 	// Init output value
4041 	* offset = 0;
4042 	* size = 0;
4043 
4044 	memset(fru, 0, sizeof(struct fru_info));
4045 	memset(&header, 0, sizeof(struct fru_header));
4046 
4047 	/*
4048 	* get info about this FRU
4049 	*/
4050 	memset(msg_data, 0, 4);
4051 	msg_data[0] = id;
4052 
4053 	memset(&req, 0, sizeof(req));
4054 	req.msg.netfn = IPMI_NETFN_STORAGE;
4055 	req.msg.cmd = GET_FRU_INFO;
4056 	req.msg.data = msg_data;
4057 	req.msg.data_len = 1;
4058 
4059 	rsp = intf->sendrecv(intf, &req);
4060 	if (rsp == NULL) {
4061 		printf(" Device not present (No Response)\n");
4062 		return -1;
4063 	}
4064 	if (rsp->ccode > 0) {
4065 		printf(" Device not present (%s)\n",
4066 			val2str(rsp->ccode, completion_code_vals));
4067 		return -1;
4068 	}
4069 
4070 	memset(&fru, 0, sizeof(fru));
4071 	fru->size = (rsp->data[1] << 8) | rsp->data[0];
4072 	fru->access = rsp->data[2] & 0x1;
4073 
4074 	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
4075 		fru->size, fru->access ? "words" : "bytes");
4076 
4077 	if (fru->size < 1) {
4078 		lprintf(LOG_ERR, " Invalid FRU size %d", fru->size);
4079 		return -1;
4080 	}
4081 
4082 	/*
4083 	* retrieve the FRU header
4084 	*/
4085 	msg_data[0] = id;
4086 	msg_data[1] = 0;
4087 	msg_data[2] = 0;
4088 	msg_data[3] = 8;
4089 
4090 	memset(&req, 0, sizeof(req));
4091 	req.msg.netfn = IPMI_NETFN_STORAGE;
4092 	req.msg.cmd = GET_FRU_DATA;
4093 	req.msg.data = msg_data;
4094 	req.msg.data_len = 4;
4095 
4096 	rsp = intf->sendrecv(intf, &req);
4097 	if (rsp == NULL) {
4098 		printf(" Device not present (No Response)\n");
4099 		return 1;
4100 	}
4101 	if (rsp->ccode > 0) {
4102 		printf(" Device not present (%s)\n",
4103 				val2str(rsp->ccode, completion_code_vals));
4104 		return 1;
4105 	}
4106 
4107 	if (verbose > 1)
4108 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
4109 
4110 	memcpy(&header, rsp->data + 1, 8);
4111 
4112 	if (header.version != 1) {
4113 		lprintf(LOG_ERR, " Unknown FRU header version 0x%02x",
4114 			header.version);
4115 		return -1;
4116 	}
4117 
4118 	lprintf(LOG_DEBUG, "fru.header.version:         0x%x",
4119 		header.version);
4120 	lprintf(LOG_DEBUG, "fru.header.offset.internal: 0x%x",
4121 		header.offset.internal * 8);
4122 	lprintf(LOG_DEBUG, "fru.header.offset.chassis:  0x%x",
4123 		header.offset.chassis * 8);
4124 	lprintf(LOG_DEBUG, "fru.header.offset.board:    0x%x",
4125 		header.offset.board * 8);
4126 	lprintf(LOG_DEBUG, "fru.header.offset.product:  0x%x",
4127 		header.offset.product * 8);
4128 	lprintf(LOG_DEBUG, "fru.header.offset.multi:    0x%x",
4129 		header.offset.multi * 8);
4130 
4131 	if((header.offset.internal*8) == 0)
4132 	{
4133 		* size = 0;
4134 		* offset = 0;
4135 	}
4136 	else
4137 	{
4138 		(* offset) = (header.offset.internal*8);
4139 
4140 		if(header.offset.chassis != 0)
4141 		{
4142 			(* size) = ((header.offset.chassis*8)-(* offset));
4143 		}
4144 		else if(header.offset.board != 0)
4145 		{
4146 			(* size) = ((header.offset.board*8)-(* offset));
4147 		}
4148 		else if(header.offset.product != 0)
4149 		{
4150 			(* size) = ((header.offset.product*8)-(* offset));
4151 		}
4152 		else if(header.offset.multi != 0)
4153 		{
4154 			(* size) = ((header.offset.multi*8)-(* offset));
4155 		}
4156 		else
4157 		{
4158 			(* size) = (fru->size - (* offset));
4159 		}
4160 	}
4161 	return 0;
4162 }
4163 
4164 /* ipmi_fru_info_internal_use -  print internal use info
4165 *
4166 * @intf:   ipmi interface
4167 * @id:     fru id
4168 *
4169 * returns -1 on error
4170 * returns 0 if successful
4171 * returns 1 if device not present
4172 */
4173 static int
4174 ipmi_fru_info_internal_use(struct ipmi_intf * intf, uint8_t id)
4175 {
4176 	struct fru_info fru;
4177 	uint16_t size;
4178 	uint16_t offset;
4179 	int rc = 0;
4180 
4181 	rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset);
4182 
4183 	if(rc == 0)
4184 	{
4185 		lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset);
4186 		printf(          "Internal Use Area Size  : %i\n", size);
4187 	}
4188 	else
4189 	{
4190 		lprintf(LOG_ERR, "Cannot access internal use area");
4191 		return -1;
4192 	}
4193 	return 0;
4194 }
4195 
4196 /* ipmi_fru_help - print help text for FRU subcommand
4197  *
4198  * returns void
4199  */
4200 void
4201 ipmi_fru_help()
4202 {
4203 	lprintf(LOG_NOTICE,
4204 			"FRU Commands:  print read write upgEkey edit internaluse get");
4205 } /* ipmi_fru_help() */
4206 
4207 /* ipmi_fru_read_internal_use -  print internal use are in hex or file
4208 *
4209 * @intf:   ipmi interface
4210 * @id:     fru id
4211 *
4212 * returns -1 on error
4213 * returns 0 if successful
4214 * returns 1 if device not present
4215 */
4216 static int
4217 ipmi_fru_read_internal_use(struct ipmi_intf * intf, uint8_t id, char * pFileName)
4218 {
4219 	struct fru_info fru;
4220 	uint16_t size;
4221 	uint16_t offset;
4222 	int rc = 0;
4223 
4224 	rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset);
4225 
4226 	if(rc == 0)
4227 	{
4228 		uint8_t * frubuf;
4229 
4230 		lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset);
4231 		printf(          "Internal Use Area Size  : %i\n", size);
4232 
4233 		frubuf = malloc( size );
4234 		if(frubuf)
4235 		{
4236 			rc = read_fru_area_section(intf, &fru, id, offset, size, frubuf);
4237 
4238 			if(rc == 0)
4239 			{
4240 				if(pFileName == NULL)
4241 				{
4242 					uint16_t counter;
4243 					for(counter = 0; counter < size; counter ++)
4244 					{
4245 						if((counter % 16) == 0)
4246 							printf("\n%02i- ", (counter / 16));
4247 						printf("%02X ", frubuf[counter]);
4248 					}
4249 				}
4250 				else
4251 				{
4252 					FILE * pFile;
4253 					pFile = fopen(pFileName,"wb");
4254 					if (pFile)
4255 					{
4256 						fwrite(frubuf, size, 1, pFile);
4257 						printf("Done\n");
4258 					}
4259 					else
4260 					{
4261 						lprintf(LOG_ERR, "Error opening file %s\n", pFileName);
4262 						free(frubuf);
4263 						frubuf = NULL;
4264 						return -1;
4265 					}
4266 					fclose(pFile);
4267 				}
4268 			}
4269 			printf("\n");
4270 
4271 			free(frubuf);
4272 			frubuf = NULL;
4273 		}
4274 
4275 	}
4276 	else
4277 	{
4278 		lprintf(LOG_ERR, "Cannot access internal use area");
4279 	}
4280 	return 0;
4281 }
4282 
4283 /* ipmi_fru_write_internal_use   -  print internal use are in hex or file
4284 *
4285 * @intf:   ipmi interface
4286 * @id:     fru id
4287 *
4288 * returns -1 on error
4289 * returns 0 if successful
4290 * returns 1 if device not present
4291 */
4292 static int
4293 ipmi_fru_write_internal_use(struct ipmi_intf * intf, uint8_t id, char * pFileName)
4294 {
4295 	struct fru_info fru;
4296 	uint16_t size;
4297 	uint16_t offset;
4298 	int rc = 0;
4299 
4300 	rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset);
4301 
4302 	if(rc == 0)
4303 	{
4304 		uint8_t * frubuf;
4305 		FILE * fp;
4306 		uint32_t fileLength = 0;
4307 
4308 		lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset);
4309 		printf(            "Internal Use Area Size  : %i\n", size);
4310 
4311 		fp = fopen(pFileName, "r");
4312 
4313 		if(fp)
4314 		{
4315 			/* Retreive file length, check if it's fits the Eeprom Size */
4316 			fseek(fp, 0 ,SEEK_END);
4317 			fileLength = ftell(fp);
4318 
4319 			lprintf(LOG_ERR, "File Size: %i", fileLength);
4320 			lprintf(LOG_ERR, "Area Size: %i", size);
4321 			if(fileLength != size)
4322 			{
4323 				lprintf(LOG_ERR, "File size does not fit Eeprom Size");
4324 				fclose(fp);
4325 				fp = NULL;
4326 			}
4327 			else
4328 			{
4329 				fseek(fp, 0 ,SEEK_SET);
4330 			}
4331 		}
4332 
4333 		if(fp)
4334 		{
4335 			frubuf = malloc( size );
4336 			if(frubuf)
4337 			{
4338 				uint16_t fru_read_size;
4339 				fru_read_size = fread(frubuf, 1, size, fp);
4340 
4341 				if(fru_read_size == size)
4342 				{
4343 					rc = write_fru_area(intf, &fru, id, 0, offset, size, frubuf);
4344 
4345 					if(rc == 0)
4346 					{
4347 						lprintf(LOG_INFO, "Done\n");
4348 					}
4349 				}
4350 				else
4351 				{
4352 					lprintf(LOG_ERR, "Unable to read file: %i\n", fru_read_size);
4353 				}
4354 
4355 				free(frubuf);
4356 				frubuf = NULL;
4357 			}
4358 			fclose(fp);
4359 			fp = NULL;
4360 		}
4361 	}
4362 	else
4363 	{
4364 		lprintf(LOG_ERR, "Cannot access internal use area");
4365 	}
4366 	return 0;
4367 }
4368 
4369 int
4370 ipmi_fru_main(struct ipmi_intf * intf, int argc, char ** argv)
4371 {
4372 	int rc = 0;
4373 	uint8_t fru_id = 0;
4374 
4375 	if (argc < 1) {
4376 		rc = ipmi_fru_print_all(intf);
4377 	}
4378 	else if (strncmp(argv[0], "help", 4) == 0) {
4379 		ipmi_fru_help();
4380 		return 0;
4381 	}
4382 	else if (strncmp(argv[0], "print", 5) == 0 ||
4383 		strncmp(argv[0], "list", 4) == 0) {
4384 		if (argc > 1) {
4385 			if (strcmp(argv[1], "help") == 0) {
4386 				lprintf(LOG_NOTICE, "fru print [fru id] - print information about FRU(s)");
4387 				return 0;
4388 			}
4389 
4390 			if (is_fru_id(argv[1], &fru_id) != 0)
4391 				return (-1);
4392 
4393 			rc = __ipmi_fru_print(intf, fru_id);
4394 		} else {
4395 			rc = ipmi_fru_print_all(intf);
4396 		}
4397 	}
4398 	else if (!strncmp(argv[0], "read", 5)) {
4399 		if (argc > 1 && strcmp(argv[1], "help") == 0) {
4400 			ipmi_fru_read_help();
4401 			return 0;
4402 		} else if (argc < 3) {
4403 			lprintf(LOG_ERR, "Not enough parameters given.");
4404 			ipmi_fru_read_help();
4405 			return (-1);
4406 		}
4407 
4408 		if (is_fru_id(argv[1], &fru_id) != 0)
4409 			return (-1);
4410 
4411 		/* There is a file name in the parameters */
4412 		if (is_valid_filename(argv[2]) != 0)
4413 				return (-1);
4414 
4415 		if (verbose) {
4416 			printf("FRU ID           : %d\n", fru_id);
4417 			printf("FRU File         : %s\n", argv[2]);
4418 		}
4419 		/* TODO - rc is missing */
4420 		ipmi_fru_read_to_bin(intf, argv[2], fru_id);
4421 	}
4422 	else if (!strncmp(argv[0], "write", 5)) {
4423 		if (argc > 1 && strcmp(argv[1], "help") == 0) {
4424 			ipmi_fru_write_help();
4425 			return 0;
4426 		} else if (argc < 3) {
4427 			lprintf(LOG_ERR, "Not enough parameters given.");
4428 			ipmi_fru_write_help();
4429 			return (-1);
4430 		}
4431 
4432 		if (is_fru_id(argv[1], &fru_id) != 0)
4433 			return (-1);
4434 
4435 		/* There is a file name in the parameters */
4436 		if (is_valid_filename(argv[2]) != 0)
4437 				return (-1);
4438 
4439 		if (verbose) {
4440 			printf("FRU ID           : %d\n", fru_id);
4441 			printf("FRU File         : %s\n", argv[2]);
4442 		}
4443 		/* TODO - rc is missing */
4444 		ipmi_fru_write_from_bin(intf, argv[2], fru_id);
4445 	}
4446 	else if (!strncmp(argv[0], "upgEkey", 7)) {
4447 		if (argc > 1 && strcmp(argv[1], "help") == 0) {
4448 			ipmi_fru_upgekey_help();
4449 			return 0;
4450 		} else if (argc < 3) {
4451 			lprintf(LOG_ERR, "Not enough parameters given.");
4452 			ipmi_fru_upgekey_help();
4453 			return (-1);
4454 		}
4455 
4456 		if (is_fru_id(argv[1], &fru_id) != 0)
4457 			return (-1);
4458 
4459 		/* There is a file name in the parameters */
4460 		if (is_valid_filename(argv[2]) != 0)
4461 				return (-1);
4462 
4463 		rc = ipmi_fru_upg_ekeying(intf, argv[2], fru_id);
4464 	}
4465 	else if (!strncmp(argv[0], "internaluse", 11)) {
4466 		if (argc > 1 && strcmp(argv[1], "help") == 0) {
4467 			ipmi_fru_internaluse_help();
4468 			return 0;
4469 		}
4470 
4471 		if ( (argc >= 3) && (!strncmp(argv[2], "info", 4)) ) {
4472 
4473 			if (is_fru_id(argv[1], &fru_id) != 0)
4474 				return (-1);
4475 
4476 			rc = ipmi_fru_info_internal_use(intf, fru_id);
4477 		}
4478 		else if ( (argc >= 3) && (!strncmp(argv[2], "print", 5)) ) {
4479 
4480 			if (is_fru_id(argv[1], &fru_id) != 0)
4481 				return (-1);
4482 
4483 			rc = ipmi_fru_read_internal_use(intf, fru_id, NULL);
4484 		}
4485 		else if ( (argc >= 4) && (!strncmp(argv[2], "read", 4)) ) {
4486 
4487 			if (is_fru_id(argv[1], &fru_id) != 0)
4488 				return (-1);
4489 
4490 			/* There is a file name in the parameters */
4491 			if (is_valid_filename(argv[3]) != 0)
4492 					return (-1);
4493 
4494 			lprintf(LOG_DEBUG, "FRU ID           : %d", fru_id);
4495 			lprintf(LOG_DEBUG, "FRU File         : %s", argv[3]);
4496 
4497 			rc = ipmi_fru_read_internal_use(intf, fru_id, argv[3]);
4498 		}
4499 		else if ( (argc >= 4) && (!strncmp(argv[2], "write", 5)) ) {
4500 
4501 			if (is_fru_id(argv[1], &fru_id) != 0)
4502 				return (-1);
4503 
4504 			/* There is a file name in the parameters */
4505 			if (is_valid_filename(argv[3]) != 0)
4506 					return (-1);
4507 
4508 			lprintf(LOG_DEBUG, "FRU ID           : %d", fru_id);
4509 			lprintf(LOG_DEBUG, "FRU File         : %s", argv[3]);
4510 
4511 			rc = ipmi_fru_write_internal_use(intf, fru_id, argv[3]);
4512 		} else {
4513 			lprintf(LOG_ERR,
4514 					"Either unknown command or not enough parameters given.");
4515 			ipmi_fru_internaluse_help();
4516 			return (-1);
4517 		}
4518 	}
4519 	else if (!strncmp(argv[0], "edit", 4)) {
4520 		if (argc > 1 && strcmp(argv[1], "help") == 0) {
4521 			ipmi_fru_edit_help();
4522 			return 0;
4523 		} else if (argc < 2) {
4524 			lprintf(LOG_ERR, "Not enough parameters given.");
4525 			ipmi_fru_edit_help();
4526 			return (-1);
4527 		}
4528 
4529 		if (argc >= 2) {
4530 			if (is_fru_id(argv[1], &fru_id) != 0)
4531 				return (-1);
4532 
4533 			if (verbose) {
4534 				printf("FRU ID           : %d\n", fru_id);
4535 			}
4536 		} else {
4537 			printf("Using default FRU ID: %d\n", fru_id);
4538 		}
4539 
4540 		if (argc >= 3) {
4541 			if (!strncmp(argv[2], "field", 5)) {
4542 				if (argc != 6) {
4543 					lprintf(LOG_ERR, "Not enough parameters given.");
4544 					ipmi_fru_edit_help();
4545 					return (-1);
4546 				}
4547 				rc = ipmi_fru_set_field_string(intf, fru_id, *argv[3], *argv[4],
4548 						(char *) argv[5]);
4549 			} else if (!strncmp(argv[2], "oem", 3)) {
4550 				rc = ipmi_fru_edit_multirec(intf, fru_id, argc, argv);
4551 			} else {
4552 				lprintf(LOG_ERR, "Invalid command: %s", argv[2]);
4553 				ipmi_fru_edit_help();
4554 				return (-1);
4555 			}
4556 		} else {
4557 			rc = ipmi_fru_edit_multirec(intf, fru_id, argc, argv);
4558 		}
4559 	}
4560 	else if (!strncmp(argv[0], "get", 4)) {
4561 		if (argc > 1 && (strncmp(argv[1], "help", 4) == 0)) {
4562 			ipmi_fru_get_help();
4563 			return 0;
4564 		} else if (argc < 2) {
4565 			lprintf(LOG_ERR, "Not enough parameters given.");
4566 			ipmi_fru_get_help();
4567 			return (-1);
4568 		}
4569 
4570 		if (argc >= 2) {
4571 			if (is_fru_id(argv[1], &fru_id) != 0)
4572 				return (-1);
4573 
4574 			if (verbose) {
4575 				printf("FRU ID           : %d\n", fru_id);
4576 			}
4577 		} else {
4578 			printf("Using default FRU ID: %d\n", fru_id);
4579 		}
4580 
4581 		if (argc >= 3) {
4582 			if (!strncmp(argv[2], "oem", 3)) {
4583 				rc = ipmi_fru_get_multirec(intf, fru_id, argc, argv);
4584 			} else {
4585 				lprintf(LOG_ERR, "Invalid command: %s", argv[2]);
4586 				ipmi_fru_get_help();
4587 				return (-1);
4588 			}
4589 		} else {
4590 			rc = ipmi_fru_get_multirec(intf, fru_id, argc, argv);
4591 		}
4592 	}
4593 	else {
4594 		lprintf(LOG_ERR, "Invalid FRU command: %s", argv[0]);
4595 		ipmi_fru_help();
4596 		return (-1);
4597 	}
4598 
4599 	return rc;
4600 }
4601 
4602 /* ipmi_fru_set_field_string -  Set a field string to a new value, Need to be the same size.  If
4603 *                              size if not equal, the function ipmi_fru_set_field_string_rebuild
4604 *                              will be called.
4605 *
4606 * @intf:       ipmi interface
4607 * @id:         fru id
4608 * @f_type:    Type of the Field : c=Chassis b=Board p=Product
4609 * @f_index:   findex of the field, zero indexed.
4610 * @f_string:  NULL terminated string
4611 *
4612 * returns -1 on error
4613 * returns 1 if successful
4614 */
4615 static int
4616 ipmi_fru_set_field_string(struct ipmi_intf * intf, uint8_t fruId, uint8_t
4617 f_type, uint8_t f_index, char *f_string)
4618 {
4619 	struct ipmi_rs *rsp;
4620 	struct ipmi_rq req;
4621 
4622 	struct fru_info fru;
4623 	struct fru_header header;
4624 	uint8_t msg_data[4];
4625 	uint8_t checksum;
4626 	int i = 0;
4627 	int rc = 1;
4628 	uint8_t *fru_data = NULL;
4629 	uint8_t *fru_area = NULL;
4630 	uint32_t fru_field_offset, fru_field_offset_tmp;
4631 	uint32_t fru_section_len, header_offset;
4632 
4633 	memset(msg_data, 0, 4);
4634 	msg_data[0] = fruId;
4635 
4636 	memset(&req, 0, sizeof(req));
4637 	req.msg.netfn = IPMI_NETFN_STORAGE;
4638 	req.msg.cmd = GET_FRU_INFO;
4639 	req.msg.data = msg_data;
4640 	req.msg.data_len = 1;
4641 
4642 	rsp = intf->sendrecv(intf, &req);
4643 	if (rsp == NULL) {
4644 		printf(" Device not present (No Response)\n");
4645 		rc = (-1);
4646 		goto ipmi_fru_set_field_string_out;
4647 	}
4648 	if (rsp->ccode > 0) {
4649 		printf(" Device not present (%s)\n",
4650 			val2str(rsp->ccode, completion_code_vals));
4651 		rc = (-1);
4652 		goto ipmi_fru_set_field_string_out;
4653 	}
4654 
4655 	memset(&fru, 0, sizeof(fru));
4656 	fru.size = (rsp->data[1] << 8) | rsp->data[0];
4657 	fru.access = rsp->data[2] & 0x1;
4658 
4659 	if (fru.size < 1) {
4660 		printf(" Invalid FRU size %d", fru.size);
4661 		rc = (-1);
4662 		goto ipmi_fru_set_field_string_out;
4663 	}
4664 	/*
4665 	* retrieve the FRU header
4666 	*/
4667 	msg_data[0] = fruId;
4668 	msg_data[1] = 0;
4669 	msg_data[2] = 0;
4670 	msg_data[3] = 8;
4671 
4672 	memset(&req, 0, sizeof(req));
4673 	req.msg.netfn = IPMI_NETFN_STORAGE;
4674 	req.msg.cmd = GET_FRU_DATA;
4675 	req.msg.data = msg_data;
4676 	req.msg.data_len = 4;
4677 
4678 	rsp = intf->sendrecv(intf, &req);
4679 	if (rsp == NULL)
4680 	{
4681 		printf(" Device not present (No Response)\n");
4682 		rc = (-1);
4683 		goto ipmi_fru_set_field_string_out;
4684 	}
4685 	if (rsp->ccode > 0)
4686 	{
4687 		printf(" Device not present (%s)\n",
4688 				val2str(rsp->ccode, completion_code_vals));
4689 		rc = (-1);
4690 		goto ipmi_fru_set_field_string_out;
4691 	}
4692 
4693 	if (verbose > 1)
4694 		printbuf(rsp->data, rsp->data_len, "FRU DATA");
4695 
4696 	memcpy(&header, rsp->data + 1, 8);
4697 
4698 	if (header.version != 1)
4699 	{
4700 		printf(" Unknown FRU header version 0x%02x",
4701 			header.version);
4702 		rc = (-1);
4703 		goto ipmi_fru_set_field_string_out;
4704 	}
4705 
4706 	fru_data = malloc( fru.size );
4707 
4708 	if( fru_data == NULL )
4709 	{
4710 		printf("Out of memory!\n");
4711 		rc = (-1);
4712 		goto ipmi_fru_set_field_string_out;
4713 	}
4714 
4715 	/* Setup offset from the field type */
4716 
4717 	/* Chassis type field */
4718 	if (f_type == 'c' ) {
4719 		header_offset = (header.offset.chassis * 8);
4720 		read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data);
4721 		fru_field_offset = 3;
4722 		fru_section_len = *(fru_data + 1) * 8;
4723 	}
4724 	/* Board type field */
4725 	else if (f_type == 'b' ) {
4726 		header_offset = (header.offset.board * 8);
4727 		read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data);
4728 		fru_field_offset = 6;
4729 		fru_section_len = *(fru_data + 1) * 8;
4730 	}
4731 	/* Product type field */
4732 	else if (f_type == 'p' ) {
4733 		header_offset = (header.offset.product * 8);
4734 		read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data);
4735 		fru_field_offset = 3;
4736 		fru_section_len = *(fru_data + 1) * 8;
4737 	}
4738 	else
4739 	{
4740 		printf("Wrong field type.");
4741 		rc = (-1);
4742 		goto ipmi_fru_set_field_string_out;
4743 	}
4744 	memset(fru_data, 0, fru.size);
4745 	if( read_fru_area(intf ,&fru, fruId, header_offset ,
4746 					fru_section_len , fru_data) < 0 )
4747 	{
4748 		rc = (-1);
4749 		goto ipmi_fru_set_field_string_out;
4750 	}
4751 	/* Convert index from character to decimal */
4752 	f_index= f_index - 0x30;
4753 
4754 	/*Seek to field index */
4755 	for (i=0; i <= f_index; i++) {
4756 		fru_field_offset_tmp = fru_field_offset;
4757 		if (fru_area != NULL) {
4758 			free(fru_area);
4759 			fru_area = NULL;
4760 		}
4761 		fru_area = (uint8_t *) get_fru_area_str(fru_data, &fru_field_offset);
4762 	}
4763 
4764 	if( (fru_area == NULL )  || strlen((const char *)fru_area) == 0 ) {
4765 		printf("Field not found !\n");
4766 		rc = (-1);
4767 		goto ipmi_fru_set_field_string_out;
4768 	}
4769 
4770 	if ( strlen((const char *)fru_area) == strlen((const char *)f_string) )
4771 	{
4772 		printf("Updating Field '%s' with '%s' ...\n", fru_area, f_string );
4773 		memcpy(fru_data + fru_field_offset_tmp + 1,
4774 								f_string, strlen(f_string));
4775 
4776 		checksum = 0;
4777 		/* Calculate Header Checksum */
4778 		for( i = header_offset; i < header_offset
4779 						+ fru_section_len - 1; i ++ )
4780 		{
4781 			checksum += fru_data[i];
4782 		}
4783 		checksum = (~checksum) + 1;
4784 		fru_data[header_offset + fru_section_len - 1] = checksum;
4785 
4786 		/* Write the updated section to the FRU data; source offset => 0 */
4787 		if( write_fru_area(intf, &fru, fruId, 0,
4788 				header_offset, fru_section_len, fru_data) < 0 )
4789 		{
4790 			printf("Write to FRU data failed.\n");
4791 			rc = (-1);
4792 			goto ipmi_fru_set_field_string_out;
4793 		}
4794 	}
4795 	else {
4796 		printf("String size are not equal, resizing fru to fit new string\n");
4797 		if(
4798 				ipmi_fru_set_field_string_rebuild(intf,fruId,fru,header,f_type,f_index,f_string)
4799 		)
4800 		{
4801 			rc = (-1);
4802 			goto ipmi_fru_set_field_string_out;
4803 		}
4804 	}
4805 
4806 	ipmi_fru_set_field_string_out:
4807 	if (fru_data != NULL) {
4808 		free(fru_data);
4809 		fru_data = NULL;
4810 	}
4811 	if (fru_area != NULL) {
4812 		free(fru_area);
4813 		fru_area = NULL;
4814 	}
4815 
4816 	return rc;
4817 }
4818 
4819 /*
4820 	This function can update a string within of the following section when the size is not equal:
4821 
4822 	Chassis
4823 	Product
4824 	Board
4825 */
4826 /* ipmi_fru_set_field_string_rebuild -  Set a field string to a new value, When size are not
4827 *                                      the same size.
4828 *
4829 *  This function can update a string within of the following section when the size is not equal:
4830 *
4831 *      - Chassis
4832 *      - Product
4833 *      - Board
4834 *
4835 * @intf:     ipmi interface
4836 * @fruId:    fru id
4837 * @fru:      info about fru
4838 * @header:   contain the header of the FRU
4839 * @f_type:   Type of the Field : c=Chassis b=Board p=Product
4840 * @f_index:  findex of the field, zero indexed.
4841 * @f_string: NULL terminated string
4842 *
4843 * returns -1 on error
4844 * returns 1 if successful
4845 */
4846 
4847 #define DBG_RESIZE_FRU
4848 static int
4849 ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId,
4850 											struct fru_info fru, struct fru_header header,
4851 											uint8_t f_type, uint8_t f_index, char *f_string)
4852 {
4853 	uint8_t msg_data[4];
4854 	uint8_t checksum;
4855 	int i = 0;
4856 	uint8_t *fru_data_old = NULL;
4857 	uint8_t *fru_data_new = NULL;
4858 	uint8_t *fru_area = NULL;
4859 	uint32_t fru_field_offset, fru_field_offset_tmp;
4860 	uint32_t fru_section_len, old_section_len, header_offset;
4861 	uint32_t chassis_offset, board_offset, product_offset;
4862 	uint32_t chassis_len, board_len, product_len, product_len_new;
4863 	int      num_byte_change = 0, padding_len = 0;
4864 	uint32_t counter;
4865 	unsigned char cksum;
4866 	int rc = 1;
4867 
4868 	fru_data_old = calloc( fru.size, sizeof(uint8_t) );
4869 
4870 	fru_data_new = malloc( fru.size );
4871 
4872 	if( fru_data_old == NULL || fru_data_new == NULL )
4873 	{
4874 		printf("Out of memory!\n");
4875 		rc = (-1);
4876 		goto ipmi_fru_set_field_string_rebuild_out;
4877 	}
4878 
4879 	/*************************
4880 	1) Read ALL FRU */
4881 	printf("Read All FRU area\n");
4882 	printf("Fru Size       : %u bytes\n", fru.size);
4883 
4884 	/* Read current fru data */
4885 	read_fru_area(intf ,&fru, fruId, 0, fru.size , fru_data_old);
4886 
4887 	#ifdef DBG_RESIZE_FRU
4888 	printf("Copy to new FRU\n");
4889 	#endif
4890 
4891 	/*************************
4892 	2) Copy all FRU to new FRU */
4893 	memcpy(fru_data_new, fru_data_old, fru.size);
4894 
4895 	/* Build location of all modifiable components */
4896 	chassis_offset = (header.offset.chassis * 8);
4897 	board_offset   = (header.offset.board   * 8);
4898 	product_offset = (header.offset.product * 8);
4899 
4900 	/* Retrieve length of all modifiable components */
4901 	chassis_len    =  *(fru_data_old + chassis_offset + 1) * 8;
4902 	board_len      =  *(fru_data_old + board_offset   + 1) * 8;
4903 	product_len    =  *(fru_data_old + product_offset + 1) * 8;
4904 	product_len_new = product_len;
4905 
4906 	/* Chassis type field */
4907 	if (f_type == 'c' )
4908 	{
4909 		header_offset    = chassis_offset;
4910 		fru_field_offset = chassis_offset + 3;
4911 		fru_section_len  = chassis_len;
4912 	}
4913 	/* Board type field */
4914 	else if (f_type == 'b' )
4915 	{
4916 		header_offset    = board_offset;
4917 		fru_field_offset = board_offset + 6;
4918 		fru_section_len  = board_len;
4919 	}
4920 	/* Product type field */
4921 	else if (f_type == 'p' )
4922 	{
4923 		header_offset    = product_offset;
4924 		fru_field_offset = product_offset + 3;
4925 		fru_section_len  = product_len;
4926 	}
4927 	else
4928 	{
4929 		printf("Wrong field type.");
4930 		rc = (-1);
4931 		goto ipmi_fru_set_field_string_rebuild_out;
4932 	}
4933 
4934 	/* Keep length for future old section display */
4935 	old_section_len = fru_section_len;
4936 
4937 	/*************************
4938 	3) Seek to field index */
4939 	for (i = 0;i <= f_index; i++) {
4940 		fru_field_offset_tmp = fru_field_offset;
4941 		if (fru_area != NULL) {
4942 			free(fru_area);
4943 			fru_area = NULL;
4944 		}
4945 		fru_area = (uint8_t *) get_fru_area_str(fru_data_old, &fru_field_offset);
4946 	}
4947 
4948 	if( (fru_area == NULL )  || strlen((const char *)fru_area) == 0 ) {
4949 		printf("Field not found (1)!\n");
4950 		rc = (-1);
4951 		goto ipmi_fru_set_field_string_rebuild_out;
4952 	}
4953 
4954 	#ifdef DBG_RESIZE_FRU
4955 	printf("Section Length: %u\n", fru_section_len);
4956 	#endif
4957 
4958 	/*************************
4959 	4) Check number of padding bytes and bytes changed */
4960 	for(counter = 2; counter < fru_section_len; counter ++)
4961 	{
4962 		if(*(fru_data_old + (header_offset + fru_section_len - counter)) == 0)
4963 			padding_len ++;
4964 		else
4965 			break;
4966 	}
4967 	num_byte_change = strlen(f_string) - strlen(fru_area);
4968 
4969 	#ifdef DBG_RESIZE_FRU
4970 	printf("Padding Length: %u\n", padding_len);
4971 	printf("NumByte Change: %i\n", num_byte_change);
4972 	printf("Start SecChnge: %x\n", *(fru_data_old + fru_field_offset_tmp));
4973 	printf("End SecChnge  : %x\n", *(fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1));
4974 
4975 	printf("Start Section : %x\n", *(fru_data_old + header_offset));
4976 	printf("End Sec wo Pad: %x\n", *(fru_data_old + header_offset + fru_section_len - 2 - padding_len));
4977 	printf("End Section   : %x\n", *(fru_data_old + header_offset + fru_section_len - 1));
4978 	#endif
4979 
4980 	/* Calculate New Padding Length */
4981 	padding_len -= num_byte_change;
4982 
4983 	#ifdef DBG_RESIZE_FRU
4984 	printf("New Padding Length: %i\n", padding_len);
4985 	#endif
4986 
4987 	/*************************
4988 	5) Check if section must be resize.  This occur when padding length is not between 0 and 7 */
4989 	if( (padding_len < 0) || (padding_len >= 8))
4990 	{
4991 		uint32_t remaining_offset = ((header.offset.product * 8) + product_len);
4992 		int change_size_by_8;
4993 
4994 		if(padding_len >= 8)
4995 		{
4996 			/* Section must be set smaller */
4997 			change_size_by_8 = ((padding_len) / 8) * (-1);
4998 		}
4999 		else
5000 		{
5001 			/* Section must be set bigger */
5002 			change_size_by_8 = 1 + (((padding_len+1) / 8) * (-1));
5003 		}
5004 
5005 		/* Recalculate padding and section length base on the section changes */
5006 		fru_section_len += (change_size_by_8 * 8);
5007 		padding_len     += (change_size_by_8 * 8);
5008 
5009 		#ifdef DBG_RESIZE_FRU
5010 		printf("change_size_by_8: %i\n", change_size_by_8);
5011 		printf("New Padding Length: %i\n", padding_len);
5012 		printf("change_size_by_8: %i\n", change_size_by_8);
5013 		printf("header.offset.board: %i\n", header.offset.board);
5014 		#endif
5015 
5016 		/* Must move sections */
5017 		/* Section that can be modified are as follow
5018 			Chassis
5019 			Board
5020 			product */
5021 
5022 		/* Chassis type field */
5023 		if (f_type == 'c' )
5024 		{
5025 			printf("Moving Section Chassis, from %i to %i\n",
5026 						((header.offset.board) * 8),
5027 						((header.offset.board + change_size_by_8) * 8)
5028 					);
5029 			memcpy(
5030 						(fru_data_new + ((header.offset.board + change_size_by_8) * 8)),
5031 						(fru_data_old + (header.offset.board) * 8),
5032 						board_len
5033 					);
5034 			header.offset.board   += change_size_by_8;
5035 		}
5036 		/* Board type field */
5037 		if ((f_type == 'c' ) || (f_type == 'b' ))
5038 		{
5039 			printf("Moving Section Product, from %i to %i\n",
5040 						((header.offset.product) * 8),
5041 						((header.offset.product + change_size_by_8) * 8)
5042 					);
5043 			memcpy(
5044 						(fru_data_new + ((header.offset.product + change_size_by_8) * 8)),
5045 						(fru_data_old + (header.offset.product) * 8),
5046 						product_len
5047 					);
5048 			header.offset.product += change_size_by_8;
5049 		}
5050 
5051 		/* Adjust length of the section */
5052 		if (f_type == 'c')
5053 		{
5054 			*(fru_data_new + chassis_offset + 1) += change_size_by_8;
5055 		}
5056 		else if( f_type == 'b')
5057 		{
5058 			*(fru_data_new + board_offset + 1)   += change_size_by_8;
5059 		}
5060 		else if( f_type == 'p')
5061 		{
5062 			*(fru_data_new + product_offset + 1) += change_size_by_8;
5063 			product_len_new = *(fru_data_new + product_offset + 1) * 8;
5064 		}
5065 
5066 		/* Rebuild Header checksum */
5067 		{
5068 			unsigned char * pfru_header = (unsigned char *) &header;
5069 			header.checksum = 0;
5070 			for(counter = 0; counter < (sizeof(struct fru_header) -1); counter ++)
5071 			{
5072 				header.checksum += pfru_header[counter];
5073 			}
5074 			header.checksum = (0 - header.checksum);
5075 			memcpy(fru_data_new, pfru_header, sizeof(struct fru_header));
5076 		}
5077 
5078 		/* Move remaining sections in 1 copy */
5079 		printf("Moving Remaining Bytes (Multi-Rec , etc..), from %i to %i\n",
5080 					remaining_offset,
5081 					((header.offset.product) * 8) + product_len_new
5082 				);
5083 		if(((header.offset.product * 8) + product_len_new - remaining_offset) < 0)
5084 		{
5085 			memcpy(
5086 						fru_data_new + (header.offset.product * 8) + product_len_new,
5087 						fru_data_old + remaining_offset,
5088 						fru.size - remaining_offset
5089 					);
5090 		}
5091 		else
5092 		{
5093 			memcpy(
5094 						fru_data_new + (header.offset.product * 8) + product_len_new,
5095 						fru_data_old + remaining_offset,
5096 						fru.size - ((header.offset.product * 8) + product_len_new)
5097 					);
5098 		}
5099 	}
5100 
5101 	/* Update only if it's fits padding length as defined in the spec, otherwise, it's an internal
5102 	error */
5103 	/*************************
5104 	6) Update Field and sections */
5105 	if( (padding_len >=0) && (padding_len < 8))
5106 	{
5107 		/* Do not requires any change in other section */
5108 
5109 		/* Change field length */
5110 		printf(
5111 			"Updating Field : '%s' with '%s' ... (Length from '%d' to '%d')\n",
5112 			fru_area, f_string,
5113 			(int)*(fru_data_old + fru_field_offset_tmp),
5114 			(int)(0xc0 + strlen(f_string)));
5115 		*(fru_data_new + fru_field_offset_tmp) = (0xc0 + strlen(f_string));
5116 		memcpy(fru_data_new + fru_field_offset_tmp + 1, f_string, strlen(f_string));
5117 
5118 		/* Copy remaing bytes in section */
5119 #ifdef DBG_RESIZE_FRU
5120 		printf("Copying remaining of sections: %d \n",
5121 		 (int)((fru_data_old + header_offset + fru_section_len - 1) -
5122 		 (fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1)));
5123 #endif
5124 
5125 		memcpy((fru_data_new + fru_field_offset_tmp + 1 +
5126 			strlen(f_string)),
5127 			(fru_data_old + fru_field_offset_tmp + 1 +
5128 			strlen(fru_area)),
5129 		((fru_data_old + header_offset + fru_section_len - 1) -
5130 		(fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1)));
5131 
5132 		/* Add Padding if required */
5133 		for(counter = 0; counter < padding_len; counter ++)
5134 		{
5135 			*(fru_data_new + header_offset + fru_section_len - 1 -
5136 			  padding_len + counter) = 0;
5137 		}
5138 
5139 		/* Calculate New Checksum */
5140 		cksum = 0;
5141 		for( counter = 0; counter <fru_section_len-1; counter ++ )
5142 		{
5143 			cksum += *(fru_data_new + header_offset + counter);
5144 		}
5145 		*(fru_data_new + header_offset + fru_section_len - 1) = (0 - cksum);
5146 
5147 		#ifdef DBG_RESIZE_FRU
5148 		printf("Calculate New Checksum: %x\n", (0 - cksum));
5149 		#endif
5150 
5151 		/****** ENABLE to show section modified before and after ********/
5152 		#if 0
5153 		printf("Section: ");
5154 		for( counter = 0; counter <old_section_len; counter ++ )
5155 		{
5156 			if((counter %16) == 0)
5157 			{
5158 				printf("\n");
5159 			}
5160 			printf( "%02X ", *(fru_data_old + header_offset + counter) );
5161 		}
5162 		printf("\n");
5163 
5164 		printf("Section: ");
5165 		for( counter = 0; counter <fru_section_len; counter ++ )
5166 		{
5167 			if((counter %16) == 0)
5168 			{
5169 				printf("\n");
5170 			}
5171 			printf( "%02X ", *(fru_data_new + header_offset + counter) );
5172 		}
5173 		printf("\n");
5174 		#endif
5175 	}
5176 	else
5177 	{
5178 		printf( "Internal error, padding length %i (must be from 0 to 7) ", padding_len );
5179 		rc = (-1);
5180 		goto ipmi_fru_set_field_string_rebuild_out;
5181 	}
5182 
5183 	/*************************
5184 	7) Finally, write new FRU */
5185 	printf("Writing new FRU.\n");
5186 	if( write_fru_area( intf, &fru, fruId, 0, 0, fru.size, fru_data_new ) < 0 )
5187 	{
5188 		printf("Write to FRU data failed.\n");
5189 		rc = (-1);
5190 		goto ipmi_fru_set_field_string_rebuild_out;
5191 	}
5192 
5193 	printf("Done.\n");
5194 
5195 	ipmi_fru_set_field_string_rebuild_out:
5196 	if (fru_area != NULL) {
5197 		free(fru_area);
5198 		fru_area = NULL;
5199 	}
5200 	if (fru_data_new != NULL) {
5201 		free(fru_data_new);
5202 		fru_data_new = NULL;
5203 	}
5204 	if (fru_data_old != NULL) {
5205 		free(fru_data_old);
5206 		fru_data_old = NULL;
5207 	}
5208 
5209 	return rc;
5210 }
5211