diff options
Diffstat (limited to 'lib/ipmi_fru.c')
| -rw-r--r-- | lib/ipmi_fru.c | 5209 | 
1 files changed, 5209 insertions, 0 deletions
| diff --git a/lib/ipmi_fru.c b/lib/ipmi_fru.c new file mode 100644 index 0000000..1b2e0cd --- /dev/null +++ b/lib/ipmi_fru.c @@ -0,0 +1,5209 @@ +/* +* Copyright (c) 2003 Sun Microsystems, Inc.  All Rights Reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* Redistribution of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* +* Redistribution in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* Neither the name of Sun Microsystems, Inc. or the names of +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* This software is provided "AS IS," without a warranty of any kind. +* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, +* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A +* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. +* SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE +* FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING +* OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL +* SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, +* OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR +* PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF +* LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, +* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +*/ + +#include <ipmitool/ipmi.h> +#include <ipmitool/log.h> +#include <ipmitool/helper.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_fru.h> +#include <ipmitool/ipmi_mc.h> +#include <ipmitool/ipmi_sdr.h> +#include <ipmitool/ipmi_strings.h>  /* IANA id strings */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <errno.h> + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#define FRU_MULTIREC_CHUNK_SIZE     (255 + sizeof(struct fru_multirec_header)) + +extern int verbose; + +static void ipmi_fru_read_to_bin(struct ipmi_intf * intf, char * pFileName, uint8_t fruId); +static void ipmi_fru_write_from_bin(struct ipmi_intf * intf, char * pFileName, uint8_t fruId); +static int ipmi_fru_upg_ekeying(struct ipmi_intf * intf, char * pFileName, uint8_t fruId); +static int ipmi_fru_get_multirec_location_from_fru(struct ipmi_intf * intf, uint8_t fruId, +							struct fru_info *pFruInfo, uint32_t * pRetLocation, +							uint32_t * pRetSize); +static int ipmi_fru_get_multirec_from_file(char * pFileName, uint8_t * pBufArea, +						uint32_t size, uint32_t offset); +static int ipmi_fru_get_multirec_size_from_file(char * pFileName, uint32_t * pSize, uint32_t * pOffset); +int ipmi_fru_get_adjust_size_from_buffer(uint8_t *pBufArea, uint32_t *pSize); +static void ipmi_fru_picmg_ext_print(uint8_t * fru_data, int off, int length); + +static int ipmi_fru_set_field_string(struct ipmi_intf * intf, unsigned +						char fruId, uint8_t f_type, uint8_t f_index, char *f_string); +static int +ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId, +											struct fru_info fru, struct fru_header header, +											uint8_t f_type, uint8_t f_index, char *f_string); + +static void +fru_area_print_multirec_bloc(struct ipmi_intf * intf, struct fru_info * fru, +			uint8_t id, uint32_t offset); +int +read_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id, +			uint32_t offset, uint32_t length, uint8_t *frubuf); +void free_fru_bloc(t_ipmi_fru_bloc *bloc); + +/* get_fru_area_str  -  Parse FRU area string from raw data +* +* @data:   raw FRU data +* @offset: offset into data for area +* +* returns pointer to FRU area string +*/ +char * get_fru_area_str(uint8_t * data, uint32_t * offset) +{ +	static const char bcd_plus[] = "0123456789 -.:,_"; +	char * str; +	int len, off, size, i, j, k, typecode; +	union { +		uint32_t bits; +		char chars[4]; +	} u; + +	size = 0; +	off = *offset; + +	/* bits 6:7 contain format */ +	typecode = ((data[off] & 0xC0) >> 6); + +	// printf("Typecode:%i\n", typecode); +	/* bits 0:5 contain length */ +	len = data[off++]; +	len &= 0x3f; + +	switch (typecode) { +	case 0:           /* 00b: binary/unspecified */ +		/* hex dump -> 2x length */ +		size = (len*2); +		break; +	case 2:           /* 10b: 6-bit ASCII */ +		/* 4 chars per group of 1-3 bytes */ +		size = ((((len+2)*4)/3) & ~3); +		break; +	case 3:           /* 11b: 8-bit ASCII */ +	case 1:           /* 01b: BCD plus */ +		/* no length adjustment */ +		size = len; +		break; +	} + +	if (size < 1) { +		*offset = off; +		return NULL; +	} +	str = malloc(size+1); +	if (str == NULL) +		return NULL; +	memset(str, 0, size+1); + +	if (len == 0) { +		str[0] = '\0'; +		*offset = off; +		return str; +	} + +	switch (typecode) { +	case 0:        /* Binary */ +		strncpy(str, buf2str(&data[off], len), len*2); +		break; + +	case 1:        /* BCD plus */ +		for (k=0; k<len; k++) +			str[k] = bcd_plus[(data[off+k] & 0x0f)]; +		str[k] = '\0'; +		break; + +	case 2:        /* 6-bit ASCII */ +		for (i=j=0; i<len; i+=3) { +			u.bits = 0; +			k = ((len-i) < 3 ? (len-i) : 3); +#if WORDS_BIGENDIAN +			u.chars[3] = data[off+i]; +			u.chars[2] = (k > 1 ? data[off+i+1] : 0); +			u.chars[1] = (k > 2 ? data[off+i+2] : 0); +#define CHAR_IDX 3 +#else +			memcpy((void *)&u.bits, &data[off+i], k); +#define CHAR_IDX 0 +#endif +			for (k=0; k<4; k++) { +				str[j++] = ((u.chars[CHAR_IDX] & 0x3f) + 0x20); +				u.bits >>= 6; +			} +		} +		str[j] = '\0'; +		break; + +	case 3: +		memcpy(str, &data[off], len); +		str[len] = '\0'; +		break; +	} + +	off += len; +	*offset = off; + +	return str; +} + +/* is_valid_filename - checks file/path supplied by user + * + * input_filename - user input string + * + * returns   0  if path is ok + * returns (-1) if path is NULL + * returns (-2) if path is too short + * returns (-3) if path is too long + */ +int +is_valid_filename(const char *input_filename) +{ +	if (input_filename == NULL) { +		lprintf(LOG_ERR, "ERROR: NULL pointer passed."); +		return (-1); +	} + +	if (strlen(input_filename) < 1) { +		lprintf(LOG_ERR, "File/path is invalid."); +		return (-2); +	} + +	if (strlen(input_filename) >= 512) { +		lprintf(LOG_ERR, "File/path must be shorter than 512 bytes."); +		return (-3); +	} + +	return 0; +} /* is_valid_filename() */ + +/* build_fru_bloc  -  build fru bloc for write protection +* +* @intf:     ipmi interface +* @fru_info: information about FRU device +* @id      : Fru id +* @soffset : Source offset      (from buffer) +* @doffset : Destination offset (in device) +* @length  : Size of data to write (in bytes) +* @pFrubuf : Pointer on data to write +* +* returns 0 on success +* returns -1 on error +*/ +#define FRU_NUM_BLOC_COMMON_HEADER  6 +t_ipmi_fru_bloc * +build_fru_bloc(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id) +{ +	t_ipmi_fru_bloc * p_first, * p_bloc, * p_new; +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	struct fru_header header; +	struct fru_multirec_header rec_hdr; +	uint8_t msg_data[4]; +	uint32_t off; +	uint16_t i; + +	/* +	* get COMMON Header format +	*/ +	msg_data[0] = id; +	msg_data[1] = 0; +	msg_data[2] = 0; +	msg_data[3] = 8; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_DATA; +	req.msg.data = msg_data; +	req.msg.data_len = 4; + +	rsp = intf->sendrecv(intf, &req); + +	if (rsp == NULL) { +		lprintf(LOG_ERR, " Device not present (No Response)"); +		return NULL; +	} + +	if (rsp->ccode > 0) { +		lprintf(LOG_ERR," Device not present (%s)", +				val2str(rsp->ccode, completion_code_vals)); +		return NULL; +	} + +	if (verbose > 1) { +		printbuf(rsp->data, rsp->data_len, "FRU DATA"); +	} + +	memcpy(&header, rsp->data + 1, 8); + +	/* verify header checksum */ +	if (ipmi_csum((uint8_t *)&header, 8)) { +		lprintf(LOG_ERR, " Bad header checksum"); +		return NULL; +	} + +	if (header.version != 1) { +		lprintf(LOG_ERR, " Unknown FRU header version 0x%02x", header.version); +		return NULL; +	} + +	/****************************************** +		Malloc and fill up the bloc contents +	*******************************************/ + +	// Common header +	p_first = malloc(sizeof(struct ipmi_fru_bloc)); +	if (!p_first) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return NULL; +	} + +	p_bloc = p_first; +	p_bloc->next = NULL; +	p_bloc->start= 0; +	p_bloc->size = fru->size; +	strcpy((char *)p_bloc->blocId, "Common Header Section"); + +	for (i = 0; i < 4; i++) { +		if (header.offsets[i]) { +			p_new = malloc(sizeof(struct ipmi_fru_bloc)); +			if (!p_new) { +				lprintf(LOG_ERR, "ipmitool: malloc failure"); +				free_fru_bloc(p_first); +				return NULL; +			} + +			p_new->next = NULL; +			p_new->start = header.offsets[i] * 8; +			p_new->size = fru->size - p_new->start; + +			strncpy((char *)p_new->blocId, section_id[i], sizeof(p_new->blocId)); +			/* Make sure string is null terminated */ +			p_new->blocId[sizeof(p_new->blocId)-1] = 0; + +			p_bloc->next = p_new; +			p_bloc->size = p_new->start - p_bloc->start; +			p_bloc = p_new; +		} +	} + +	// Multi +	if (header.offset.multi) { +		off = header.offset.multi * 8; + +		do { +			/* +			 * check for odd offset for the case of fru devices +			 * accessed by words +			 */ +			if (fru->access && (off & 1)) { +				lprintf(LOG_ERR, " Unaligned offset for a block: %d", off); +				/* increment offset */ +				off++; +				break; +			} + +			if (read_fru_area(intf, fru, id, off, 5, +					(uint8_t *) &rec_hdr) < 0) { +				break; +			} + +			p_new = malloc(sizeof(struct ipmi_fru_bloc)); +			if (!p_new) { +				lprintf(LOG_ERR, "ipmitool: malloc failure"); +				free_fru_bloc(p_first); +				return NULL; +			} + +			p_new->next = NULL; +			p_new->start = off; +			p_new->size = fru->size - p_new->start; +			sprintf((char *)p_new->blocId, "Multi-Rec Area: Type %i", +					rec_hdr.type); + +			p_bloc->next = p_new; +			p_bloc->size = p_new->start - p_bloc->start; +			p_bloc = p_new; + +			off += rec_hdr.len + sizeof(struct fru_multirec_header); + +			/* verify record header */ +			if (ipmi_csum((uint8_t *)&rec_hdr, +					sizeof(struct fru_multirec_header))) { +				/* can't reliably judge for the rest space */ +				break; +			} +		} while (!(rec_hdr.format & 0x80) && (off < fru->size)); + +		lprintf(LOG_DEBUG,"Multi-Record area ends at: %i (%xh)", off, off); + +		if (fru->size > off) { +			// Bloc for remaining space +			p_new = malloc(sizeof(struct ipmi_fru_bloc)); +			if (!p_new) { +				lprintf(LOG_ERR, "ipmitool: malloc failure"); +				free_fru_bloc(p_first); +				return NULL; +			} + +			p_new->next = NULL; +			p_new->start = off; +			p_new->size = fru->size - p_new->start; +			strcpy((char *)p_new->blocId, "Unused space"); + +			p_bloc->next = p_new; +			p_bloc->size = p_new->start - p_bloc->start; +		} +	} + +	/* Dump blocs */ +	for(p_bloc = p_first, i = 0; p_bloc; p_bloc = p_bloc->next) { +		lprintf(LOG_DEBUG ,"Bloc Numb : %i", i++); +		lprintf(LOG_DEBUG ,"Bloc Id   : %s", p_bloc->blocId); +		lprintf(LOG_DEBUG ,"Bloc Start: %i", p_bloc->start); +		lprintf(LOG_DEBUG ,"Bloc Size : %i", p_bloc->size); +		lprintf(LOG_DEBUG ,""); +	} + +	return p_first; +} + +void +free_fru_bloc(t_ipmi_fru_bloc *bloc) +{ +	t_ipmi_fru_bloc * del; + +	while (bloc) { +		del = bloc; +		bloc = bloc->next; +		free(del); +		del = NULL; +	} +} + +/* + * write FRU[doffset:length] from the pFrubuf[soffset:length] + * rc=1 on success +**/ +int +write_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id, +					uint16_t soffset,  uint16_t doffset, +					uint16_t length, uint8_t *pFrubuf) +{ +	uint16_t tmp, finish; +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	uint8_t msg_data[255+3]; +	uint16_t writeLength; +	uint16_t found_bloc = 0; + +	finish = doffset + length;        /* destination offset */ +	if (finish > fru->size) +	{ +		lprintf(LOG_ERROR, "Return error"); +		return -1; +	} + +	if (fru->access && ((doffset & 1) || (length & 1))) { +		lprintf(LOG_ERROR, "Odd offset or length specified"); +		return (-1); +	} + +	t_ipmi_fru_bloc * fru_bloc = build_fru_bloc(intf, fru, id); +	t_ipmi_fru_bloc * saved_fru_bloc = fru_bloc; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = SET_FRU_DATA; +	req.msg.data = msg_data; + +	/* initialize request size only once */ +	if (fru->max_write_size == 0) { +		uint16_t max_rq_size = ipmi_intf_get_max_request_data_size(intf); + +		/* validate lower bound of the maximum request data size */ +		if (max_rq_size <= 3) { +			lprintf(LOG_ERROR, "Maximum request size is too small to send " +					"a write request"); +			return -1; +		} + +		/* +		 * Write FRU Info command returns the number of written bytes in +		 * a single byte field. +		 */ +		if (max_rq_size - 3 > 255) { +			/*  Limit the max write size with 255 bytes. */ +			fru->max_write_size = 255; +		} else { +			/* subtract 1 byte for FRU ID an 2 bytes for offset */ +			fru->max_write_size = max_rq_size - 3; +		} + +		/* check word access */ +		if (fru->access) { +			fru->max_write_size &= ~1; +		} +	} + +	do { +		uint16_t end_bloc; +		uint8_t protected_bloc = 0; + +		/* Write per bloc, try to find the end of a bloc*/ +		while (fru_bloc && fru_bloc->start + fru_bloc->size <= doffset) { +			fru_bloc = fru_bloc->next; +			found_bloc++; +		} + +		if (fru_bloc && fru_bloc->start + fru_bloc->size < finish) { +			end_bloc = fru_bloc->start + fru_bloc->size; +		} else { +			end_bloc = finish; +		} + +		/* calculate write length */ +		tmp = end_bloc - doffset; + +		/* check that write length is more than maximum request size */ +		if (tmp > fru->max_write_size) { +			writeLength = fru->max_write_size; +		} else { +			writeLength = tmp; +		} + +		/* copy fru data */ +		memcpy(&msg_data[3], pFrubuf + soffset, writeLength); + +		/* check word access */ +		if (fru->access) { +			writeLength &= ~1; +		} + +		tmp = doffset; +		if (fru->access) { +			tmp >>= 1; +		} + +		msg_data[0] = id; +		msg_data[1] = (uint8_t)tmp; +		msg_data[2] = (uint8_t)(tmp >> 8); +		req.msg.data_len = writeLength + 3; + +		if(fru_bloc) { +			lprintf(LOG_INFO,"Writing %d bytes (Bloc #%i: %s)", +					writeLength, found_bloc, fru_bloc->blocId); +		} else { +			lprintf(LOG_INFO,"Writing %d bytes", writeLength); +		} + +		rsp = intf->sendrecv(intf, &req); +		if (!rsp) { +			break; +		} + +		if (rsp->ccode == 0xc7 || rsp->ccode == 0xc8 || rsp->ccode == 0xca) { +			if (fru->max_write_size > 8) { +				fru->max_write_size -= 8; +				lprintf(LOG_INFO, "Retrying FRU write with request size %d", +						fru->max_write_size); +				continue; +			} +		} else if(rsp->ccode == 0x80) { +			rsp->ccode = 0; +			// Write protected section +			protected_bloc = 1; +		} + +		if (rsp->ccode > 0) +			break; + +		if (protected_bloc == 0) { +			// Write OK, bloc not protected, continue +			lprintf(LOG_INFO,"Wrote %d bytes", writeLength); +			doffset += writeLength; +			soffset += writeLength; +		} else { +			if(fru_bloc) { +				// Bloc protected, advise user and jump over protected bloc +				lprintf(LOG_INFO, +						"Bloc [%s] protected at offset: %i (size %i bytes)", +						fru_bloc->blocId, fru_bloc->start, fru_bloc->size); +				lprintf(LOG_INFO,"Jumping over this bloc"); +			} else { +				lprintf(LOG_INFO, +						"Remaining FRU is protected following offset: %i", +						doffset); +			} +			soffset += end_bloc - doffset; +			doffset = end_bloc; +		} +	} while (doffset < finish); + +	if (saved_fru_bloc) { +		free_fru_bloc(saved_fru_bloc); +	} + +	return doffset >= finish; +} + +/* read_fru_area  -  fill in frubuf[offset:length] from the FRU[offset:length] +* +* @intf:   ipmi interface +* @fru: fru info +* @id:     fru id +* @offset: offset into buffer +* @length: how much to read +* @frubuf: buffer read into +* +* returns -1 on error +* returns 0 if successful +*/ +int +read_fru_area(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id, +			uint32_t offset, uint32_t length, uint8_t *frubuf) +{ +	uint32_t off = offset, tmp, finish; +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	uint8_t msg_data[4]; + +	if (offset > fru->size) { +		lprintf(LOG_ERR, "Read FRU Area offset incorrect: %d > %d", +			offset, fru->size); +		return -1; +	} + +	finish = offset + length; +	if (finish > fru->size) { +		finish = fru->size; +		lprintf(LOG_NOTICE, "Read FRU Area length %d too large, " +			"Adjusting to %d", +			offset + length, finish - offset); +	} + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_DATA; +	req.msg.data = msg_data; +	req.msg.data_len = 4; + +	if (fru->max_read_size == 0) { +		uint16_t max_rs_size = ipmi_intf_get_max_response_data_size(intf) - 1; + +		/* validate lower bound of the maximum response data size */ +		if (max_rs_size <= 1) { +			lprintf(LOG_ERROR, "Maximum response size is too small to send " +					"a read request"); +			return -1; +		} + +		/* +		 * Read FRU Info command may read up to 255 bytes of data. +		 */ +		if (max_rs_size - 1 > 255) { +			/*  Limit the max read size with 255 bytes. */ +			fru->max_read_size = 255; +		} else { +			/* subtract 1 byte for bytes count */ +			fru->max_read_size = max_rs_size - 1; +		} + +		/* check word access */ +		if (fru->access) { +			fru->max_read_size &= ~1; +		} +	} + +	do { +		tmp = fru->access ? off >> 1 : off; +		msg_data[0] = id; +		msg_data[1] = (uint8_t)(tmp & 0xff); +		msg_data[2] = (uint8_t)(tmp >> 8); +		tmp = finish - off; +		if (tmp > fru->max_read_size) +			msg_data[3] = (uint8_t)fru->max_read_size; +		else +			msg_data[3] = (uint8_t)tmp; + +		rsp = intf->sendrecv(intf, &req); +		if (rsp == NULL) { +			lprintf(LOG_NOTICE, "FRU Read failed"); +			break; +		} +		if (rsp->ccode > 0) { +			/* if we get C8h or CAh completion code then we requested too +			* many bytes at once so try again with smaller size */ +			if ((rsp->ccode == 0xc8 || rsp->ccode == 0xca) +					&& fru->max_read_size > 8) { +				if (fru->max_read_size > 32) { +					/* subtract read length more aggressively */ +					fru->max_read_size -= 8; +				} else { +					/* subtract length less aggressively */ +					fru->max_read_size--; +				} + +				lprintf(LOG_INFO, "Retrying FRU read with request size %d", +						fru->max_read_size); +				continue; +			} + +			lprintf(LOG_NOTICE, "FRU Read failed: %s", +				val2str(rsp->ccode, completion_code_vals)); +			break; +		} + +		tmp = fru->access ? rsp->data[0] << 1 : rsp->data[0]; +		memcpy(frubuf, rsp->data + 1, tmp); +		off += tmp; +		frubuf += tmp; +		/* sometimes the size returned in the Info command +		* is too large.  return 0 so higher level function +		* still attempts to parse what was returned */ +		if (tmp == 0 && off < finish) { +			return 0; +		} +	} while (off < finish); + +	if (off < finish) { +		return -1; +	} + +	return 0; +} + +/* read_fru_area  -  fill in frubuf[offset:length] from the FRU[offset:length] +* +* @intf:   ipmi interface +* @fru: fru info +* @id:     fru id +* @offset: offset into buffer +* @length: how much to read +* @frubuf: buffer read into +* +* returns -1 on error +* returns 0 if successful +*/ +int +read_fru_area_section(struct ipmi_intf * intf, struct fru_info *fru, uint8_t id, +			uint32_t offset, uint32_t length, uint8_t *frubuf) +{ +	static uint32_t fru_data_rqst_size = 20; +	uint32_t off = offset, tmp, finish; +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	uint8_t msg_data[4]; + +	if (offset > fru->size) { +		lprintf(LOG_ERR, "Read FRU Area offset incorrect: %d > %d", +			offset, fru->size); +		return -1; +	} + +	finish = offset + length; +	if (finish > fru->size) { +		finish = fru->size; +		lprintf(LOG_NOTICE, "Read FRU Area length %d too large, " +			"Adjusting to %d", +			offset + length, finish - offset); +	} + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_DATA; +	req.msg.data = msg_data; +	req.msg.data_len = 4; + +#ifdef LIMIT_ALL_REQUEST_SIZE +	if (fru_data_rqst_size > 16) +#else +	if (fru->access && fru_data_rqst_size > 16) +#endif +		fru_data_rqst_size = 16; +	do { +		tmp = fru->access ? off >> 1 : off; +		msg_data[0] = id; +		msg_data[1] = (uint8_t)(tmp & 0xff); +		msg_data[2] = (uint8_t)(tmp >> 8); +		tmp = finish - off; +		if (tmp > fru_data_rqst_size) +			msg_data[3] = (uint8_t)fru_data_rqst_size; +		else +			msg_data[3] = (uint8_t)tmp; + +		rsp = intf->sendrecv(intf, &req); +		if (rsp == NULL) { +			lprintf(LOG_NOTICE, "FRU Read failed"); +			break; +		} +		if (rsp->ccode > 0) { +			/* if we get C7 or C8  or CA return code then we requested too +			* many bytes at once so try again with smaller size */ +			if ((rsp->ccode == 0xc7 || rsp->ccode == 0xc8 || rsp->ccode == 0xca) && +				(--fru_data_rqst_size > 8)) { +				lprintf(LOG_INFO, "Retrying FRU read with request size %d", +					fru_data_rqst_size); +				continue; +			} +			lprintf(LOG_NOTICE, "FRU Read failed: %s", +				val2str(rsp->ccode, completion_code_vals)); +			break; +		} + +		tmp = fru->access ? rsp->data[0] << 1 : rsp->data[0]; +		memcpy((frubuf + off)-offset, rsp->data + 1, tmp); +		off += tmp; + +		/* sometimes the size returned in the Info command +		* is too large.  return 0 so higher level function +		* still attempts to parse what was returned */ +		if (tmp == 0 && off < finish) +			return 0; + +	} while (off < finish); + +	if (off < finish) +		return -1; + +	return 0; +} + + +static void +fru_area_print_multirec_bloc(struct ipmi_intf * intf, struct fru_info * fru, +			uint8_t id, uint32_t offset) +{ +	uint8_t * fru_data = NULL; +	uint32_t fru_len, i; +	struct fru_multirec_header * h; +	uint32_t last_off, len; + +	i = last_off = offset; +	fru_len = 0; + +	fru_data = malloc(fru->size + 1); +	if (fru_data == NULL) { +		lprintf(LOG_ERR, " Out of memory!"); +		return; +	} + +	memset(fru_data, 0, fru->size + 1); + +	do { +		h = (struct fru_multirec_header *) (fru_data + i); + +		// read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time +		if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len))) +		{ +			len = fru->size - last_off; +			if (len > FRU_MULTIREC_CHUNK_SIZE) +				len = FRU_MULTIREC_CHUNK_SIZE; + +			if (read_fru_area(intf, fru, id, last_off, len, fru_data) < 0) +				break; + +			last_off += len; +		} + +		//printf("Bloc Numb : %i\n", counter); +		printf("Bloc Start: %i\n", i); +		printf("Bloc Size : %i\n", h->len); +		printf("\n"); + +		i += h->len + sizeof (struct fru_multirec_header); +	} while (!(h->format & 0x80)); + +	i = offset; +	do { +		h = (struct fru_multirec_header *) (fru_data + i); + +		printf("Bloc Start: %i\n", i); +		printf("Bloc Size : %i\n", h->len); +		printf("\n"); + +		i += h->len + sizeof (struct fru_multirec_header); +	} while (!(h->format & 0x80)); + +	lprintf(LOG_DEBUG ,"Multi-Record area ends at: %i (%xh)",i,i); + +	free(fru_data); +	fru_data = NULL; +} + + +/* fru_area_print_chassis  -  Print FRU Chassis Area +* +* @intf:   ipmi interface +* @fru: fru info +* @id:  fru id +* @offset: offset pointer +*/ +static void +fru_area_print_chassis(struct ipmi_intf * intf, struct fru_info * fru, +			uint8_t id, uint32_t offset) +{ +	char * fru_area; +	uint8_t * fru_data; +	uint32_t fru_len, i; +	uint8_t tmp[2]; + +	fru_len = 0; + +	/* read enough to check length field */ +	if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) { +		fru_len = 8 * tmp[1]; +	} + +	if (fru_len == 0) { +		return; +	} + +	fru_data = malloc(fru_len); +	if (fru_data == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return; +	} + +	memset(fru_data, 0, fru_len); + +	/* read in the full fru */ +	if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) { +		free(fru_data); +		fru_data = NULL; +		return; +	} + +	/* +	 * skip first two bytes which specify +	 * fru area version and fru area length +	 */ +	i = 2; + +	printf(" Chassis Type          : %s\n", + 		chassis_type_desc[fru_data[i] > + 		(sizeof(chassis_type_desc)/sizeof(chassis_type_desc[0])) - 1 ? + 		2 : fru_data[i]]); + + 	i++; + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0) { +			printf(" Chassis Part Number   : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0) { +			printf(" Chassis Serial        : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	/* read any extra fields */ +	while ((fru_data[i] != 0xc1) && (i < fru_len)) +	{ +		int j = i; +		fru_area = get_fru_area_str(fru_data, &i); +		if (fru_area != NULL) { +			if (strlen(fru_area) > 0) { +				printf(" Chassis Extra         : %s\n", fru_area); +			} +			free(fru_area); +			fru_area = NULL; +		} + +		if (i == j) { +			break; +		} +	} + +	if (fru_area != NULL) { +		free(fru_data); +		fru_data = NULL; +	} +} + +/* fru_area_print_board  -  Print FRU Board Area +* +* @intf:   ipmi interface +* @fru: fru info +* @id:  fru id +* @offset: offset pointer +*/ +static void +fru_area_print_board(struct ipmi_intf * intf, struct fru_info * fru, +			uint8_t id, uint32_t offset) +{ +	char * fru_area; +	uint8_t * fru_data; +	uint32_t fru_len; +	uint32_t i; +	time_t tval; +	uint8_t tmp[2]; + +	fru_len = 0; + +	/* read enough to check length field */ +	if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) { +		fru_len = 8 * tmp[1]; +	} + +	if (fru_len <= 0) { +		return; +	} + +	fru_data = malloc(fru_len); +	if (fru_data == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return; +	} + +	memset(fru_data, 0, fru_len); + +	/* read in the full fru */ +	if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) { +		free(fru_data); +		fru_data = NULL; +		return; +	} + +	/* +	 * skip first three bytes which specify +	 * fru area version, fru area length +	 * and fru board language +	 */ +	i = 3; + +	tval=((fru_data[i+2] << 16) + (fru_data[i+1] << 8) + (fru_data[i])); +	tval=tval * 60; +	tval=tval + secs_from_1970_1996; +	printf(" Board Mfg Date        : %s", asctime(localtime(&tval))); +	i += 3;  /* skip mfg. date time */ + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0) { +			printf(" Board Mfg             : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0) { +			printf(" Board Product         : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0) { +			printf(" Board Serial          : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0) { +			printf(" Board Part Number     : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0 && verbose > 0) { +			printf(" Board FRU ID          : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	/* read any extra fields */ +	while ((fru_data[i] != 0xc1) && (i < fru_len)) +	{ +		int j = i; +		fru_area = get_fru_area_str(fru_data, &i); +		if (fru_area != NULL) { +			if (strlen(fru_area) > 0) { +				printf(" Board Extra           : %s\n", fru_area); +			} +			free(fru_area); +			fru_area = NULL; +		} +		if (i == j) +			break; +	} + +	if (fru_area != NULL) { +		free(fru_data); +		fru_data = NULL; +	} +} + +/* fru_area_print_product  -  Print FRU Product Area +* +* @intf:   ipmi interface +* @fru: fru info +* @id:  fru id +* @offset: offset pointer +*/ +static void +fru_area_print_product(struct ipmi_intf * intf, struct fru_info * fru, +				uint8_t id, uint32_t offset) +{ +	char * fru_area; +	uint8_t * fru_data; +	uint32_t fru_len, i; +	uint8_t tmp[2]; + +	fru_len = 0; + +	/* read enough to check length field */ +	if (read_fru_area(intf, fru, id, offset, 2, tmp) == 0) { +		fru_len = 8 * tmp[1]; +	} + +	if (fru_len == 0) { +		return; +	} + +	fru_data = malloc(fru_len); +	if (fru_data == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return; +	} + +	memset(fru_data, 0, fru_len); + + +	/* read in the full fru */ +	if (read_fru_area(intf, fru, id, offset, fru_len, fru_data) < 0) { +		free(fru_data); +		fru_data = NULL; +		return; +	} + +	/* +	 * skip first three bytes which specify +	 * fru area version, fru area length +	 * and fru board language +	 */ +	i = 3; + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0) { +			printf(" Product Manufacturer  : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0) { +			printf(" Product Name          : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0) { +			printf(" Product Part Number   : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0) { +			printf(" Product Version       : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0) { +			printf(" Product Serial        : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0) { +			printf(" Product Asset Tag     : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	fru_area = get_fru_area_str(fru_data, &i); +	if (fru_area != NULL) { +		if (strlen(fru_area) > 0 && verbose > 0) { +			printf(" Product FRU ID        : %s\n", fru_area); +		} +		free(fru_area); +		fru_area = NULL; +	} + +	/* read any extra fields */ +	while ((fru_data[i] != 0xc1) && (i < fru_len)) +	{ +		int j = i; +		fru_area = get_fru_area_str(fru_data, &i); +		if (fru_area != NULL) { +			if (strlen(fru_area) > 0) { +				printf(" Product Extra         : %s\n", fru_area); +			} +			free(fru_area); +			fru_area = NULL; +		} +		if (i == j) +			break; +	} + +	if (fru_area != NULL) { +		free(fru_data); +		fru_data = NULL; +	} +} + +/* fru_area_print_multirec  -  Print FRU Multi Record Area +* +* @intf:   ipmi interface +* @fru: fru info +* @id:  fru id +* @offset: offset pointer +*/ +static void +fru_area_print_multirec(struct ipmi_intf * intf, struct fru_info * fru, +			uint8_t id, uint32_t offset) +{ +	uint8_t * fru_data; +	struct fru_multirec_header * h; +	struct fru_multirec_powersupply * ps; +	struct fru_multirec_dcoutput * dc; +	struct fru_multirec_dcload * dl; +	uint16_t peak_capacity; +	uint8_t peak_hold_up_time; +	uint32_t last_off; + +	last_off = offset; + +	fru_data = malloc(FRU_MULTIREC_CHUNK_SIZE); +	if (fru_data == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return; +	} + +	memset(fru_data, 0, FRU_MULTIREC_CHUNK_SIZE); + +	h = (struct fru_multirec_header *) (fru_data); + +	do { +		if (read_fru_area(intf, fru, id, last_off, sizeof(*h), fru_data) < 0) { +			break; +		} + +		if (h->len && read_fru_area(intf, fru, id, +				last_off + sizeof(*h), h->len, fru_data + sizeof(*h)) < 0) { +			break; +		} + +		last_off += h->len + sizeof(*h); + +		switch (h->type) { +		case FRU_RECORD_TYPE_POWER_SUPPLY_INFORMATION: +			ps = (struct fru_multirec_powersupply *) +				(fru_data + sizeof(struct fru_multirec_header)); + +#if WORDS_BIGENDIAN +			ps->capacity      = BSWAP_16(ps->capacity); +			ps->peak_va    = BSWAP_16(ps->peak_va); +			ps->lowend_input1 = BSWAP_16(ps->lowend_input1); +			ps->highend_input1   = BSWAP_16(ps->highend_input1); +			ps->lowend_input2 = BSWAP_16(ps->lowend_input2); +			ps->highend_input2   = BSWAP_16(ps->highend_input2); +			ps->combined_capacity   = BSWAP_16(ps->combined_capacity); +			ps->peak_cap_ht      = BSWAP_16(ps->peak_cap_ht); +#endif +			peak_hold_up_time = (ps->peak_cap_ht & 0xf000) >> 12; +			peak_capacity     = ps->peak_cap_ht & 0x0fff; + +			printf (" Power Supply Record\n"); +			printf ("  Capacity                   : %d W\n", +				ps->capacity); +			printf ("  Peak VA                    : %d VA\n", +				ps->peak_va); +			printf ("  Inrush Current             : %d A\n", +				ps->inrush_current); +			printf ("  Inrush Interval            : %d ms\n", +				ps->inrush_interval); +			printf ("  Input Voltage Range 1      : %d-%d V\n", +				ps->lowend_input1 / 100, ps->highend_input1 / 100); +			printf ("  Input Voltage Range 2      : %d-%d V\n", +				ps->lowend_input2 / 100, ps->highend_input2 / 100); +			printf ("  Input Frequency Range      : %d-%d Hz\n", +				ps->lowend_freq, ps->highend_freq); +			printf ("  A/C Dropout Tolerance      : %d ms\n", +				ps->dropout_tolerance); +			printf ("  Flags                      : %s%s%s%s%s\n", +				ps->predictive_fail ? "'Predictive fail' " : "", +				ps->pfc ? "'Power factor correction' " : "", +				ps->autoswitch ? "'Autoswitch voltage' " : "", +				ps->hotswap ? "'Hot swap' " : "", +				ps->predictive_fail ? ps->rps_threshold ? +				ps->tach ? "'Two pulses per rotation'" : "'One pulse per rotation'" : +				ps->tach ? "'Failure on pin de-assertion'" : "'Failure on pin assertion'" : ""); +			printf ("  Peak capacity              : %d W\n", +				peak_capacity); +			printf ("  Peak capacity holdup       : %d s\n", +				peak_hold_up_time); +			if (ps->combined_capacity == 0) +				printf ("  Combined capacity          : not specified\n"); +			else +				printf ("  Combined capacity          : %d W (%s and %s)\n", +					ps->combined_capacity, +					combined_voltage_desc [ps->combined_voltage1], +					combined_voltage_desc [ps->combined_voltage2]); +			if (ps->predictive_fail) +				printf ("  Fan lower threshold        : %d RPS\n", +					ps->rps_threshold); +			break; + +		case FRU_RECORD_TYPE_DC_OUTPUT: +			dc = (struct fru_multirec_dcoutput *) +				(fru_data + sizeof(struct fru_multirec_header)); + +#if WORDS_BIGENDIAN +			dc->nominal_voltage  = BSWAP_16(dc->nominal_voltage); +			dc->max_neg_dev      = BSWAP_16(dc->max_neg_dev); +			dc->max_pos_dev      = BSWAP_16(dc->max_pos_dev); +			dc->ripple_and_noise = BSWAP_16(dc->ripple_and_noise); +			dc->min_current      = BSWAP_16(dc->min_current); +			dc->max_current      = BSWAP_16(dc->max_current); +#endif + +			printf (" DC Output Record\n"); +			printf ("  Output Number              : %d\n", +				dc->output_number); +			printf ("  Standby power              : %s\n", +				dc->standby ? "Yes" : "No"); +			printf ("  Nominal voltage            : %.2f V\n", +				(double) dc->nominal_voltage / 100); +			printf ("  Max negative deviation     : %.2f V\n", +				(double) dc->max_neg_dev / 100); +			printf ("  Max positive deviation     : %.2f V\n", +				(double) dc->max_pos_dev / 100); +			printf ("  Ripple and noise pk-pk     : %d mV\n", +				dc->ripple_and_noise); +			printf ("  Minimum current draw       : %.3f A\n", +				(double) dc->min_current / 1000); +			printf ("  Maximum current draw       : %.3f A\n", +				(double) dc->max_current / 1000); +			break; + +		case FRU_RECORD_TYPE_DC_LOAD: +			dl = (struct fru_multirec_dcload *) +				(fru_data + sizeof(struct fru_multirec_header)); + +#if WORDS_BIGENDIAN +			dl->nominal_voltage  = BSWAP_16(dl->nominal_voltage); +			dl->min_voltage      = BSWAP_16(dl->min_voltage); +			dl->max_voltage      = BSWAP_16(dl->max_voltage); +			dl->ripple_and_noise = BSWAP_16(dl->ripple_and_noise); +			dl->min_current      = BSWAP_16(dl->min_current); +			dl->max_current      = BSWAP_16(dl->max_current); +#endif + +			printf (" DC Load Record\n"); +			printf ("  Output Number              : %d\n", +				dl->output_number); +			printf ("  Nominal voltage            : %.2f V\n", +				(double) dl->nominal_voltage / 100); +			printf ("  Min voltage allowed        : %.2f V\n", +				(double) dl->min_voltage / 100); +			printf ("  Max voltage allowed        : %.2f V\n", +				(double) dl->max_voltage / 100); +			printf ("  Ripple and noise pk-pk     : %d mV\n", +				dl->ripple_and_noise); +			printf ("  Minimum current load       : %.3f A\n", +				(double) dl->min_current / 1000); +			printf ("  Maximum current load       : %.3f A\n", +				(double) dl->max_current / 1000); +			break; +		case FRU_RECORD_TYPE_OEM_EXTENSION: +			{ +				struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *) +										&fru_data[sizeof(struct fru_multirec_header)]; +				uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16; + +				/* Now makes sure this is really PICMG record */ + +				if( iana == IPMI_OEM_PICMG ){ +					printf("  PICMG Extension Record\n"); +					ipmi_fru_picmg_ext_print(fru_data, +													sizeof(struct fru_multirec_header), +													h->len); +				} +				/* FIXME: Add OEM record support here */ +				else{ +					printf("  OEM (%s) Record\n", val2str( iana, ipmi_oem_info)); +				} +			} +			break; +		} +	} while (!(h->format & 0x80)); + +	lprintf(LOG_DEBUG ,"Multi-Record area ends at: %i (%xh)", last_off, last_off); + +	free(fru_data); +} + +/* ipmi_fru_query_new_value  -  Query new values to replace original FRU content +* +* @data:   FRU data +* @offset: offset of the bytes to be modified in data +* @len:    size of the modified data +* +* returns : TRUE if data changed +* returns : FALSE if data not changed +*/ +int ipmi_fru_query_new_value(uint8_t *data,int offset, size_t len) +{ +	int status=FALSE; +	int ret; +	char answer; + +	printf("Would you like to change this value <y/n> ? "); +	ret = scanf("%c", &answer); +	if (ret != 1) { +		return FALSE; +	} + +	if( answer == 'y' || answer == 'Y' ){ +		int i; +		unsigned int *holder; + +		holder = malloc(len); +		printf( +		 "Enter hex values for each of the %d entries (lsb first), " +		 "hit <enter> between entries\n", (int)len); + +		/* I can't assign scanf' %x into a single char */ +		for( i=0;i<len;i++ ){ +			ret = scanf("%x", holder+i); +			if (ret != 1) { +				free(holder); +				return FALSE; +			} +		} +		for( i=0;i<len;i++ ){ +			data[offset++] = (unsigned char) *(holder+i); +		} +		/* &data[offset++] */ +		free(holder); +		holder = NULL; +		status = TRUE; +	} +	else{ +		printf("Entered %c\n",answer); +	} + +	return status; +} + +/* ipmi_fru_oemkontron_edit  - +*  Query new values to replace original FRU content +*  This is a generic enough to support any type of 'OEM' record +*  because the user supplies 'IANA number' , 'record Id' and 'record' version' +* +* However, the parser must have 'apriori' knowledge of the record format +* The currently supported record is : +* +*    IANA          : 15000  (Kontron) +*    RECORD ID     : 3 +*    RECORD VERSION: 0 (or 1) +* +* I would have like to put that stuff in an OEM specific file, but apart for +* the record format information, all commands are really standard 'FRU' command +* +* +* @data:   FRU data +* @offset: start of the current multi record (start of header) +* @len:    len of the current record (excluding header) +* @h:      pointer to record header +* @oh:     pointer to OEM /PICMG header +* +* returns: TRUE if data changed +* returns: FALSE if data not changed +*/ +#define OEM_KONTRON_INFORMATION_RECORD 3 + +#define EDIT_OEM_KONTRON_COMPLETE_ARG_COUNT    12 +#define GET_OEM_KONTRON_COMPLETE_ARG_COUNT     5 +/* +./src/ipmitool  fru edit 0 +oem 15000 3 0 name instance FIELD1 FIELD2 FIELD3 crc32 +*/ + +#define OEM_KONTRON_SUBCOMMAND_ARG_POS   2 +#define OEM_KONTRON_IANA_ARG_POS         3 +#define OEM_KONTRON_RECORDID_ARG_POS     4 +#define OEM_KONTRON_FORMAT_ARG_POS       5 +#define OEM_KONTRON_NAME_ARG_POS         6 +#define OEM_KONTRON_INSTANCE_ARG_POS     7 +#define OEM_KONTRON_VERSION_ARG_POS      8 +#define OEM_KONTRON_BUILDDATE_ARG_POS    9 +#define OEM_KONTRON_UPDATEDATE_ARG_POS   10 +#define OEM_KONTRON_CRC32_ARG_POS        11 + +#define OEM_KONTRON_FIELD_SIZE          8 +#define OEM_KONTRON_VERSION_FIELD_SIZE 10 + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif +typedef struct OemKontronInformationRecordV0{ +	uint8_t field1TypeLength; +	uint8_t field1[OEM_KONTRON_FIELD_SIZE]; +	uint8_t field2TypeLength; +	uint8_t field2[OEM_KONTRON_FIELD_SIZE]; +	uint8_t field3TypeLength; +	uint8_t field3[OEM_KONTRON_FIELD_SIZE]; +	uint8_t crcTypeLength; +	uint8_t crc32[OEM_KONTRON_FIELD_SIZE]; +}tOemKontronInformationRecordV0; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(0) +#endif + + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif +typedef struct OemKontronInformationRecordV1{ +	uint8_t field1TypeLength; +	uint8_t field1[OEM_KONTRON_VERSION_FIELD_SIZE]; +	uint8_t field2TypeLength; +	uint8_t field2[OEM_KONTRON_FIELD_SIZE]; +	uint8_t field3TypeLength; +	uint8_t field3[OEM_KONTRON_FIELD_SIZE]; +	uint8_t crcTypeLength; +	uint8_t crc32[OEM_KONTRON_FIELD_SIZE]; +}tOemKontronInformationRecordV1; +#ifdef HAVE_PRAGMA_PACK +#pragma pack(0) +#endif + +/* +./src/ipmitool  fru get 0 oem iana 3 + +*/ + +static void ipmi_fru_oemkontron_get( int argc, char ** argv,uint8_t * fru_data, +												int off,int len, +												struct fru_multirec_header *h, +												struct fru_multirec_oem_header *oh) +{ +	static int badParams=FALSE; +	int start = off; +	int offset = start; +	int length = len; +	int i; +	offset += sizeof(struct fru_multirec_oem_header); + +	if(!badParams){ +		/* the 'OEM' field is already checked in caller */ +		if( argc > OEM_KONTRON_SUBCOMMAND_ARG_POS ){ +			if(strncmp("oem", argv[OEM_KONTRON_SUBCOMMAND_ARG_POS],3)){ +				printf("usage: fru get <id> <oem>\n"); +				badParams = TRUE; +				return; +			} +		} +		if( argc<GET_OEM_KONTRON_COMPLETE_ARG_COUNT ){ +			printf("usage: oem <iana> <recordid>\n"); +			printf("usage: oem 15000 3\n"); +			badParams = TRUE; +			return; +		} +	} + +	if(!badParams){ + +		if(oh->record_id == OEM_KONTRON_INFORMATION_RECORD ) { + +			uint8_t version; + +			printf("Kontron OEM Information Record\n"); +			version = oh->record_version; + +			int blockstart; +			uint8_t blockCount; +			uint8_t blockIndex=0; + +			unsigned int matchInstance = 0; +			uint8_t instance = 0; +			 +			if (str2uchar(argv[OEM_KONTRON_INSTANCE_ARG_POS], &instance) != 0) { +				lprintf(LOG_ERR, +						"Instance argument '%s' is either invalid or out of range.", +						argv[OEM_KONTRON_INSTANCE_ARG_POS]); +				badParams = TRUE; +				return; +			} + +			blockCount = fru_data[offset++]; + +			for(blockIndex=0;blockIndex<blockCount;blockIndex++){ +				void * pRecordData; +				uint8_t nameLen; + +				blockstart = offset; +				nameLen = ( fru_data[offset++] &= 0x3F ); +				printf("  Name: %*.*s\n",nameLen, nameLen, (const char *)(fru_data+offset)); + +				offset+=nameLen; + +				pRecordData = &fru_data[offset]; + +				printf("  Record Version: %d\n", version); +				if( version == 0 ) +				{ +					printf("  Version: %*.*s\n", +						OEM_KONTRON_FIELD_SIZE, +						OEM_KONTRON_FIELD_SIZE, +						((tOemKontronInformationRecordV0 *) pRecordData)->field1); +					printf("  Build Date: %*.*s\n", +						OEM_KONTRON_FIELD_SIZE, +						OEM_KONTRON_FIELD_SIZE, +						((tOemKontronInformationRecordV0 *) pRecordData)->field2); +					printf("  Update Date: %*.*s\n", +						OEM_KONTRON_FIELD_SIZE, +						OEM_KONTRON_FIELD_SIZE, +						((tOemKontronInformationRecordV0 *) pRecordData)->field3); +					printf("  Checksum: %*.*s\n\n", +						OEM_KONTRON_FIELD_SIZE, +						OEM_KONTRON_FIELD_SIZE, +						((tOemKontronInformationRecordV0 *) pRecordData)->crc32); +					matchInstance++; +					offset+= sizeof(tOemKontronInformationRecordV0); +					offset++; +				} +				else if ( version == 1 ) +				{ +					printf("  Version: %*.*s\n", +						OEM_KONTRON_VERSION_FIELD_SIZE, +						OEM_KONTRON_VERSION_FIELD_SIZE, +						((tOemKontronInformationRecordV1 *) pRecordData)->field1); +					printf("  Build Date: %*.*s\n", +						OEM_KONTRON_FIELD_SIZE, +						OEM_KONTRON_FIELD_SIZE, +						((tOemKontronInformationRecordV1 *) pRecordData)->field2); +					printf("  Update Date: %*.*s\n", +						OEM_KONTRON_FIELD_SIZE, +						OEM_KONTRON_FIELD_SIZE, +						((tOemKontronInformationRecordV1 *) pRecordData)->field3); +					printf("  Checksum: %*.*s\n\n", +						OEM_KONTRON_FIELD_SIZE, +						OEM_KONTRON_FIELD_SIZE, +						((tOemKontronInformationRecordV1 *) pRecordData)->crc32); +					matchInstance++; +					offset+= sizeof(tOemKontronInformationRecordV1); +					offset++; +				} +				else +				{ +					printf ("  Unsupported version %d\n",version); +				} +			} +		} +	} +} + +static int ipmi_fru_oemkontron_edit( int argc, char ** argv,uint8_t * fru_data, +												int off,int len, +												struct fru_multirec_header *h, +												struct fru_multirec_oem_header *oh) +{ +	static int badParams=FALSE; +	int hasChanged = FALSE; +	int start = off; +	int offset = start; +	int length = len; +	int i; +	uint8_t record_id = 0; +	offset += sizeof(struct fru_multirec_oem_header); + +	if(!badParams){ +		/* the 'OEM' field is already checked in caller */ +		if( argc > OEM_KONTRON_SUBCOMMAND_ARG_POS ){ +			if(strncmp("oem", argv[OEM_KONTRON_SUBCOMMAND_ARG_POS],3)){ +				printf("usage: fru edit <id> <oem> <args...>\n"); +				badParams = TRUE; +				return hasChanged; +			} +		} +		if( argc<EDIT_OEM_KONTRON_COMPLETE_ARG_COUNT ){ +			printf("usage: oem <iana> <recordid> <format> <args...>\n"); +			printf("usage: oem 15000 3 0 <name> <instance> <field1>"\ +					" <field2> <field3> <crc32>\n"); +			badParams = TRUE; +			return hasChanged; +		} +		if (str2uchar(argv[OEM_KONTRON_RECORDID_ARG_POS], &record_id) != 0) { +			lprintf(LOG_ERR, +					"Record ID argument '%s' is either invalid or out of range.", +					argv[OEM_KONTRON_RECORDID_ARG_POS]); +			badParams = TRUE; +			return hasChanged; +		} +		if (record_id == OEM_KONTRON_INFORMATION_RECORD) { +			for(i=OEM_KONTRON_VERSION_ARG_POS;i<=OEM_KONTRON_CRC32_ARG_POS;i++){ +				if( (strlen(argv[i]) != OEM_KONTRON_FIELD_SIZE) && +					(strlen(argv[i]) != OEM_KONTRON_VERSION_FIELD_SIZE)) { +					printf("error: version fields must have %d characters\n", +										OEM_KONTRON_FIELD_SIZE); +					badParams = TRUE; +					return hasChanged; +				} +			} +		} +	} + +	if(!badParams){ + +		if(oh->record_id == OEM_KONTRON_INFORMATION_RECORD ) { +			uint8_t formatVersion = 0; +			uint8_t version; + +			if (str2uchar(argv[OEM_KONTRON_FORMAT_ARG_POS], &formatVersion) != 0) { +				lprintf(LOG_ERR, +						"Format argument '%s' is either invalid or out of range.", +						argv[OEM_KONTRON_FORMAT_ARG_POS]); +				badParams = TRUE; +				return hasChanged; +			} + +			printf("   Kontron OEM Information Record\n"); +			version = oh->record_version; + +			if( version == formatVersion  ){ +				int blockstart; +				uint8_t blockCount; +				uint8_t blockIndex=0; + +				uint8_t matchInstance = 0; +				uint8_t instance = 0; +				 +				if (str2uchar(argv[OEM_KONTRON_INSTANCE_ARG_POS], &instance) != 0) { +					lprintf(LOG_ERR, +							"Instance argument '%s' is either invalid or out of range.", +							argv[OEM_KONTRON_INSTANCE_ARG_POS]); +					badParams = TRUE; +					return hasChanged; +				} + +				blockCount = fru_data[offset++]; +				printf("   blockCount: %d\n",blockCount); + +				for(blockIndex=0;blockIndex<blockCount;blockIndex++){ +					void * pRecordData; +					uint8_t nameLen; + +					blockstart = offset; + +					nameLen = ( fru_data[offset++] & 0x3F ); + +					if( version == 0 || version == 1 ) +					{ +						if(!strncmp((char *)argv[OEM_KONTRON_NAME_ARG_POS], +						(const char *)(fru_data+offset),nameLen)&& (matchInstance == instance)){ + +							printf ("Found : %s\n",argv[OEM_KONTRON_NAME_ARG_POS]); +							offset+=nameLen; + +							pRecordData =  &fru_data[offset]; + +							if( version == 0 ) +							{ +								memcpy( ((tOemKontronInformationRecordV0 *) +															pRecordData)->field1 , +								argv[OEM_KONTRON_VERSION_ARG_POS] , +								OEM_KONTRON_FIELD_SIZE); +								memcpy( ((tOemKontronInformationRecordV0 *) +															pRecordData)->field2 , +								argv[OEM_KONTRON_BUILDDATE_ARG_POS], +								OEM_KONTRON_FIELD_SIZE); +								memcpy( ((tOemKontronInformationRecordV0 *) +															pRecordData)->field3 , +								argv[OEM_KONTRON_UPDATEDATE_ARG_POS], +								OEM_KONTRON_FIELD_SIZE); +								memcpy( ((tOemKontronInformationRecordV0 *) +															pRecordData)->crc32 , +							argv[OEM_KONTRON_CRC32_ARG_POS] , +							OEM_KONTRON_FIELD_SIZE); +							} +							else +							{ +								memcpy( ((tOemKontronInformationRecordV1 *) +															pRecordData)->field1 , +								argv[OEM_KONTRON_VERSION_ARG_POS] , +								OEM_KONTRON_VERSION_FIELD_SIZE); +								memcpy( ((tOemKontronInformationRecordV1 *) +															pRecordData)->field2 , +								argv[OEM_KONTRON_BUILDDATE_ARG_POS], +								OEM_KONTRON_FIELD_SIZE); +								memcpy( ((tOemKontronInformationRecordV1 *) +															pRecordData)->field3 , +								argv[OEM_KONTRON_UPDATEDATE_ARG_POS], +								OEM_KONTRON_FIELD_SIZE); +								memcpy( ((tOemKontronInformationRecordV1 *) +															pRecordData)->crc32 , +							argv[OEM_KONTRON_CRC32_ARG_POS] , +							OEM_KONTRON_FIELD_SIZE); +							} + +							matchInstance++; +							hasChanged = TRUE; +						} +						else if(!strncmp((char *)argv[OEM_KONTRON_NAME_ARG_POS], +							(const char *)(fru_data+offset), nameLen)){ +							printf ("Skipped : %s  [instance %d]\n",argv[OEM_KONTRON_NAME_ARG_POS], +									(unsigned int)matchInstance); +							matchInstance++; +							offset+=nameLen; +						} +						else { +							offset+=nameLen; +						} + +						if( version == 0 ) +						{ +							offset+= sizeof(tOemKontronInformationRecordV0); +						} +						else +						{ +							offset+= sizeof(tOemKontronInformationRecordV1); +						} +						offset++; +					} +					else +					{ +						printf ("  Unsupported version %d\n",version); +					} +				} +			} +			else{ +				printf("   Version: %d\n",version); +			} +		} +		if( hasChanged ){ + +			uint8_t record_checksum =0; +			uint8_t header_checksum =0; +			int index; + +			lprintf(LOG_DEBUG,"Initial record checksum : %x",h->record_checksum); +			lprintf(LOG_DEBUG,"Initial header checksum : %x",h->header_checksum); +			for(index=0;index<length;index++){ +				record_checksum+=  fru_data[start+index]; +			} +			/* Update Record checksum */ +			h->record_checksum =  ~record_checksum + 1; + + +			for(index=0;index<(sizeof(struct fru_multirec_header) -1);index++){ +				uint8_t data= *( (uint8_t *)h+ index); +				header_checksum+=data; +			} +			/* Update header checksum */ +			h->header_checksum =  ~header_checksum + 1; + +			lprintf(LOG_DEBUG,"Final record checksum : %x",h->record_checksum); +			lprintf(LOG_DEBUG,"Final header checksum : %x",h->header_checksum); + +			/* write back data */ +		} +	} + +	return hasChanged; +} + +/* ipmi_fru_picmg_ext_edit  -  Query new values to replace original FRU content +* +* @data:   FRU data +* @offset: start of the current multi record (start of header) +* @len:    len of the current record (excluding header) +* @h:      pointer to record header +* @oh:     pointer to OEM /PICMG header +* +* returns: TRUE if data changed +* returns: FALSE if data not changed +*/ +static int ipmi_fru_picmg_ext_edit(uint8_t * fru_data, +												int off,int len, +												struct fru_multirec_header *h, +												struct fru_multirec_oem_header *oh) +{ +	int hasChanged = FALSE; +	int start = off; +	int offset = start; +	int length = len; +	offset += sizeof(struct fru_multirec_oem_header); + +	switch (oh->record_id) +	{ +		case FRU_AMC_ACTIVATION: +			printf("    FRU_AMC_ACTIVATION\n"); +			{ +				int index=offset; +				uint16_t max_current; + +				max_current = fru_data[offset]; +				max_current |= fru_data[++offset]<<8; + +				printf("      Maximum Internal Current(@12V): %.2f A (0x%02x)\n", +								(float)max_current / 10.0f, max_current); + +				if( ipmi_fru_query_new_value(fru_data,index,2) ){ +					max_current = fru_data[index]; +					max_current |= fru_data[++index]<<8; +					printf("      New Maximum Internal Current(@12V): %.2f A (0x%02x)\n", +								(float)max_current / 10.0f, max_current); +					hasChanged = TRUE; + +				} + +				printf("      Module Activation Readiness:       %i sec.\n", fru_data[++offset]); +				printf("      Descriptor Count: %i\n", fru_data[++offset]); +				printf("\n"); + +				for (++offset; +					offset < (off + length); +					offset += sizeof(struct fru_picmgext_activation_record)) { +					struct fru_picmgext_activation_record * a = +						(struct fru_picmgext_activation_record *) &fru_data[offset]; + +					printf("        IPMB-Address:         0x%x\n", a->ibmb_addr); +					printf("        Max. Module Current:  %.2f A\n", (float)a->max_module_curr / 10.0f); + +					printf("\n"); +				} +			} +			break; + +		case FRU_AMC_CURRENT: +			printf("    FRU_AMC_CURRENT\n"); +			{ +				int index=offset; +				unsigned char current; + +				current = fru_data[index]; + +				printf("      Current draw(@12V): %.2f A (0x%02x)\n", +								(float)current / 10.0f, current); + +				if( ipmi_fru_query_new_value(fru_data, index, 1) ){ +					current = fru_data[index]; + +					printf("      New Current draw(@12V): %.2f A (0x%02x)\n", +								(float)current / 10.0f, current); +					hasChanged = TRUE; +				} +			} +			break; +	} + +	if( hasChanged ){ + +		uint8_t record_checksum =0; +		uint8_t header_checksum =0; +		int index; + +		lprintf(LOG_DEBUG,"Initial record checksum : %x",h->record_checksum); +		lprintf(LOG_DEBUG,"Initial header checksum : %x",h->header_checksum); +		for(index=0;index<length;index++){ +			record_checksum+=  fru_data[start+index]; +		} +		/* Update Record checksum */ +		h->record_checksum =  ~record_checksum + 1; + + +		for(index=0;index<(sizeof(struct fru_multirec_header) -1);index++){ +			uint8_t data= *( (uint8_t *)h+ index); +			header_checksum+=data; +		} +		/* Update header checksum */ +		h->header_checksum =  ~header_checksum + 1; + +		lprintf(LOG_DEBUG,"Final record checksum : %x",h->record_checksum); +		lprintf(LOG_DEBUG,"Final header checksum : %x",h->header_checksum); + +		/* write back data */ +	} + +	return hasChanged; +} + +/* ipmi_fru_picmg_ext_print  - prints OEM fru record (PICMG) +* +* @fru_data:  FRU data +* @offset:    offset of the bytes to be modified in data +* @length:    size of the record +* +* returns : n/a +*/ +static void ipmi_fru_picmg_ext_print(uint8_t * fru_data, int off, int length) +{ +	struct fru_multirec_oem_header *h; +	int guid_count; +	int offset = off; +	int start_offset = off; +	int i; + +	h = (struct fru_multirec_oem_header *) &fru_data[offset]; +	offset += sizeof(struct fru_multirec_oem_header); + +	switch (h->record_id) +	{ +		case FRU_PICMG_BACKPLANE_P2P: +		{ +			uint8_t index; +			unsigned int data; +			struct fru_picmgext_slot_desc *slot_d; + +			slot_d = +				(struct fru_picmgext_slot_desc*)&fru_data[offset]; +			offset += sizeof(struct fru_picmgext_slot_desc); +			printf("    FRU_PICMG_BACKPLANE_P2P\n"); + +			while (offset <= (start_offset+length)) { +				printf("\n"); +				printf("    Channel Type:  "); +				switch (slot_d->chan_type) +				{ +					case 0x00: +					case 0x07: +						printf("PICMG 2.9\n"); +						break; +					case 0x08: +						printf("Single Port Fabric IF\n"); +						break; +					case 0x09: +						printf("Double Port Fabric IF\n"); +						break; +					case 0x0a: +						printf("Full Channel Fabric IF\n"); +						break; +					case 0x0b: +						printf("Base IF\n"); +						break; +					case 0x0c: +						printf("Update Channel IF\n"); +						break; +					case 0x0d: +						printf("ShMC Cross Connect\n"); +						break; +					default: +						printf("Unknown IF (0x%x)\n", +								slot_d->chan_type); +						break; +				} +				printf("    Slot Addr.   : %02x\n", +						slot_d->slot_addr ); +				printf("    Channel Count: %i\n", +						slot_d->chn_count); + +				for (index = 0; +						index < (slot_d->chn_count); +						index++) { +					struct fru_picmgext_chn_desc *d; +					data = (fru_data[offset+0]) | +						(fru_data[offset+1] << 8) | +						(fru_data[offset+2] << 16); +					d = (struct fru_picmgext_chn_desc *)&data; +					if (verbose) { +						printf( "       " +								"Chn: %02x  ->  " +								"Chn: %02x in " +								"Slot: %02x\n", +								d->local_chn, +								d->remote_chn, +								d->remote_slot); +					} +					offset += FRU_PICMGEXT_CHN_DESC_RECORD_SIZE; +				} +				slot_d = (struct fru_picmgext_slot_desc*)&fru_data[offset]; +				offset += sizeof(struct fru_picmgext_slot_desc); +			} +		} +		break; + +		case FRU_PICMG_ADDRESS_TABLE: +		{ +			unsigned int hwaddr; +			unsigned int sitetype; +			unsigned int sitenum; +			unsigned int entries; +			unsigned int i; +			char *picmg_site_type_strings[] = { +					"AdvancedTCA Board", +					"Power Entry", +					"Shelf FRU Information", +					"Dedicated ShMC", +					"Fan Tray", +					"Fan Filter Tray", +					"Alarm", +					"AdvancedMC Module", +					"PMC", +					"Rear Transition Module"}; + + +			printf("    FRU_PICMG_ADDRESS_TABLE\n"); +			printf("      Type/Len:  0x%02x\n", fru_data[offset++]); +			printf("      Shelf Addr: "); +			for (i=0;i<20;i++) { +				printf("0x%02x ", fru_data[offset++]); +			} +			printf("\n"); + +			entries = fru_data[offset++]; +			printf("      Addr Table Entries: 0x%02x\n", entries); + +			for (i=0; i<entries; i++) { +				hwaddr = fru_data[offset]; +				sitenum = fru_data[offset + 1]; +				sitetype = fru_data[offset + 2]; +				printf( +						"        HWAddr: 0x%02x (0x%02x) SiteNum: %d SiteType: 0x%02x %s\n", +						hwaddr, hwaddr * 2, +						sitenum, sitetype, +						(sitetype < 0xa) ? +						picmg_site_type_strings[sitetype] : +						"Reserved"); +				offset += 3; +			} +		} +		break; + +		case FRU_PICMG_SHELF_POWER_DIST: +		{ +			unsigned int entries; +			unsigned int feeds; +			unsigned int feedcnt; +			unsigned int hwaddr; +			unsigned int i; +			unsigned int id; +			unsigned int j; +			unsigned int maxext; +			unsigned int maxint; +			unsigned int minexp; + +			printf("    FRU_PICMG_SHELF_POWER_DIST\n"); + +			feeds = fru_data[offset++]; +			printf("      Number of Power Feeds:   0x%02x\n", +					feeds); + +			for (i=0; i<feeds; i++) { +				printf("    Feed %d:\n", i); +				maxext = fru_data[offset] | +					(fru_data[offset+1] << 8); +				offset += 2; +				maxint = fru_data[offset] | +					(fru_data[offset+1] << 8); +				offset += 2; +				minexp = fru_data[offset]; +				offset += 1; +				entries = fru_data[offset]; +				offset += 1; + +				printf( +						"      Max External Current:   %d.%d Amps (0x%04x)\n", +						maxext / 10, maxext % 10, maxext); +				if (maxint < 0xffff) { +					printf( +							"      Max Internal Current:   %d.%d Amps (0x%04x)\n", +							maxint / 10, maxint % 10, +							maxint); +				} else { +					printf( +							"      Max Internal Current:   Not Specified\n"); +				} + +				if (minexp >= 0x48 && minexp <= 0x90) { +					printf( +							"      Min Expected Voltage:   -%02d.%dV\n", +							minexp / 2, (minexp % 2) * 5); +				} else { +					printf( +							"      Min Expected Voltage:   -%dV (actual invalid value 0x%x)\n", +							36, minexp); +				} +				for (j=0; j < entries; j++) { +					hwaddr = fru_data[offset++]; +					id = fru_data[offset++]; +					printf( +							"        FRU HW Addr: 0x%02x (0x%02x)", +							hwaddr, hwaddr * 2); +					printf( +							"   FRU ID: 0x%02x\n", +							id); +				} +			} +		} +		break; + +		case FRU_PICMG_SHELF_ACTIVATION: +		{ +			unsigned int i; +			unsigned int count = 0; + +			printf("    FRU_PICMG_SHELF_ACTIVATION\n"); +			printf( +					"      Allowance for FRU Act Readiness:   0x%02x\n", +					fru_data[offset++]); + +			count = fru_data[offset++]; +			printf( +					"      FRU activation and Power Desc Cnt: 0x%02x\n", +					count); + +			for (i=0; i<count; i++) { +				printf("         HW Addr: 0x%02x ", +						fru_data[offset++]); +				printf("         FRU ID: 0x%02x ", +						fru_data[offset++]); +				printf("         Max FRU Power: 0x%04x ", +						fru_data[offset+0] | +						(fru_data[offset+1]<<8)); +				offset += 2; +				printf("         Config: 0x%02x \n", +						fru_data[offset++]); +			} +		} +		break; + +		case FRU_PICMG_SHMC_IP_CONN: +			printf("    FRU_PICMG_SHMC_IP_CONN\n"); +			break; + +		case FRU_PICMG_BOARD_P2P: +			printf("    FRU_PICMG_BOARD_P2P\n"); + +			guid_count = fru_data[offset++]; +			printf("      GUID count: %2d\n", guid_count); +			for (i = 0 ; i < guid_count; i++ ) { +				int j; +				printf("        GUID [%2d]: 0x", i); + +				for (j=0; j < sizeof(struct fru_picmgext_guid); +						j++) { +					printf("%02x", fru_data[offset+j]); +				} + +				printf("\n"); +				offset += sizeof(struct fru_picmgext_guid); +			} +			printf("\n"); + +			for (; offset < off + length; +					offset += sizeof(struct fru_picmgext_link_desc)) { + +				/* to solve little endian /big endian problem */ +				struct fru_picmgext_link_desc *d; +				unsigned int data = (fru_data[offset+0]) | +					(fru_data[offset+1] << 8) | +					(fru_data[offset+2] << 16) | +					(fru_data[offset+3] << 24); +				d = (struct fru_picmgext_link_desc *) &data; + +				printf("      Link Grouping ID:     0x%02x\n", +						d->grouping); +				printf("      Link Type Extension:  0x%02x - ", +						d->ext); +				if (d->type == FRU_PICMGEXT_LINK_TYPE_BASE) { +					switch (d->ext) +					{ +						case 0: +							printf("10/100/1000BASE-T Link (four-pair)\n"); +							break; +						case 1: +							printf("ShMC Cross-connect (two-pair)\n"); +							break; +						default: +							printf("Unknwon\n"); +							break; +					} +				} else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET) { +					switch (d->ext) +					{ +						case 0: +							printf("Fixed 1000Base-BX\n"); +							break; +						case 1: +							printf("Fixed 10GBASE-BX4 [XAUI]\n"); +							break; +						case 2: +							printf("FC-PI\n"); +							break; +						default: +							printf("Unknwon\n"); +							break; +					} +				} else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND) { +					printf("Unknwon\n"); +				} else if (d->type == FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR) { +					printf("Unknwon\n"); +				} else if (d->type == FRU_PICMGEXT_LINK_TYPE_PCIE) { +					printf("Unknwon\n"); +				} else { +					printf("Unknwon\n"); +				} + +				printf("      Link Type:            0x%02x - ", +						d->type); +				if (d->type == 0 || d->type == 0xff) { +					printf("Reserved\n"); +				} +				else if (d->type >= 0x06 && d->type <= 0xef) { +					printf("Reserved\n"); +				} +				else if (d->type >= 0xf0 && d->type <= 0xfe) { +					printf("OEM GUID Definition\n"); +				} +				else { +					switch (d->type) +					{ +						case FRU_PICMGEXT_LINK_TYPE_BASE: +							printf("PICMG 3.0 Base Interface 10/100/1000\n"); +							break; +						case FRU_PICMGEXT_LINK_TYPE_FABRIC_ETHERNET: +							printf("PICMG 3.1 Ethernet Fabric Interface\n"); +							break; +						case FRU_PICMGEXT_LINK_TYPE_FABRIC_INFINIBAND: +							printf("PICMG 3.2 Infiniband Fabric Interface\n"); +							break; +						case FRU_PICMGEXT_LINK_TYPE_FABRIC_STAR: +							printf("PICMG 3.3 Star Fabric Interface\n"); +							break; +						case  FRU_PICMGEXT_LINK_TYPE_PCIE: +							printf("PICMG 3.4 PCI Express Fabric Interface\n"); +							break; +						default: +							printf("Invalid\n"); +							break; +					} +				} +				printf("      Link Designator: \n"); +				printf("        Port Flag:            0x%02x\n", +						d->desig_port); +				printf("        Interface:            0x%02x - ", +						d->desig_if); +				switch (d->desig_if) +				{ +					case FRU_PICMGEXT_DESIGN_IF_BASE: +						printf("Base Interface\n"); +						break; +					case FRU_PICMGEXT_DESIGN_IF_FABRIC: +						printf("Fabric Interface\n"); +						break; +					case FRU_PICMGEXT_DESIGN_IF_UPDATE_CHANNEL: +						printf("Update Channel\n"); +						break; +					case FRU_PICMGEXT_DESIGN_IF_RESERVED: +						printf("Reserved\n"); +						break; +					default: +						printf("Invalid"); +						break; +				} +				printf("        Channel Number:       0x%02x\n", +						d->desig_channel); +				printf("\n"); +			} + +			break; + +		case FRU_AMC_CURRENT: +		{ +			unsigned char current; +			printf("    FRU_AMC_CURRENT\n"); + +			current = fru_data[offset]; +			printf("      Current draw(@12V): %.2f A [ %.2f Watt ]\n", +					(float)current / 10.0f, +					(float)current / 10.0f * 12.0f); +			printf("\n"); +		} +		break; + +		case FRU_AMC_ACTIVATION: +			printf("    FRU_AMC_ACTIVATION\n"); +			{ +				uint16_t max_current; + +				max_current = fru_data[offset]; +				max_current |= fru_data[++offset]<<8; +				printf("      Maximum Internal Current(@12V): %.2f A [ %.2f Watt ]\n", +						(float)max_current / 10.0f, +						(float)max_current / 10.0f * 12.0f); + +				printf("      Module Activation Readiness:    %i sec.\n", fru_data[++offset]); +				printf("      Descriptor Count: %i\n", fru_data[++offset]); +				printf("\n"); + +				for(++offset; offset < off + length; +						offset += sizeof(struct fru_picmgext_activation_record)) +				{ +					struct fru_picmgext_activation_record *a; +					a = (struct fru_picmgext_activation_record *)&fru_data[offset]; +					printf("        IPMB-Address:         0x%x\n", +							a->ibmb_addr); +					printf("        Max. Module Current:  %.2f A\n", +							(float)a->max_module_curr / 10.0f); +					printf("\n"); +				} +			} +			break; + +		case FRU_AMC_CARRIER_P2P: +			{ +				uint16_t index; +				printf("    FRU_CARRIER_P2P\n"); +				for(; offset < off + length; ) { +					struct fru_picmgext_carrier_p2p_record * h = +						(struct fru_picmgext_carrier_p2p_record *)&fru_data[offset]; +					printf("\n"); +					printf("      Resource ID:      %i", +							(h->resource_id & 0x07)); +						printf("  Type: "); +					if ((h->resource_id>>7) == 1) { +						printf("AMC\n"); +					} else { +						printf("Local\n"); +					} +					printf("      Descriptor Count: %i\n", +							h->p2p_count); +					offset += sizeof(struct fru_picmgext_carrier_p2p_record); +					for (index = 0; index < h->p2p_count; index++) { +						/* to solve little endian /big endian problem */ +						unsigned char data[3]; +						struct fru_picmgext_carrier_p2p_descriptor * desc; +# ifndef WORDS_BIGENDIAN +						data[0] = fru_data[offset+0]; +						data[1] = fru_data[offset+1]; +						data[2] = fru_data[offset+2]; +# else +						data[0] = fru_data[offset+2]; +						data[1] = fru_data[offset+1]; +						data[2] = fru_data[offset+0]; +# endif +						desc = (struct fru_picmgext_carrier_p2p_descriptor*)&data; +						printf("        Port: %02d\t->  Remote Port: %02d\t", +								desc->local_port, desc->remote_port); +						if ((desc->remote_resource_id >> 7) == 1) { +							printf("[ AMC   ID: %02d ]\n", +									desc->remote_resource_id & 0x0F); +						} else { +							printf("[ local ID: %02d ]\n", +									desc->remote_resource_id & 0x0F); +						} +						offset += sizeof(struct fru_picmgext_carrier_p2p_descriptor); +					} +				} +			} +			break; + +		case FRU_AMC_P2P: +			{ +				unsigned int index; +				unsigned char channel_count; +				struct fru_picmgext_amc_p2p_record * h; +				printf("    FRU_AMC_P2P\n"); +				guid_count = fru_data[offset]; +				printf("      GUID count: %2d\n", guid_count); +				for (i = 0 ; i < guid_count; i++) { +					int j; +					printf("        GUID %2d: ", i); +					for (j=0; j < sizeof(struct fru_picmgext_guid); +							j++) { +						printf("%02x", fru_data[offset+j]); +						offset += sizeof(struct fru_picmgext_guid); +						printf("\n"); +					} +					h = (struct fru_picmgext_amc_p2p_record *)&fru_data[++offset]; +					printf("      %s", +							(h->record_type ? +							 "AMC Module:" : "On-Carrier Device")); +					printf("   Resource ID: %i\n", h->resource_id); +					offset += sizeof(struct fru_picmgext_amc_p2p_record); +					channel_count = fru_data[offset++]; +					printf("       Descriptor Count: %i\n", +							channel_count); +					for (index = 0; index < channel_count; index++) { +						unsigned int data; +						struct fru_picmgext_amc_channel_desc_record *d; +						/* pack the data in little endian format. +						 * Stupid intel... +						 */ +						data = fru_data[offset] | +							(fru_data[offset + 1] << 8) | +							(fru_data[offset + 2] << 16); +						d = (struct fru_picmgext_amc_channel_desc_record *)&data; +						printf("        Lane 0 Port: %i\n", +								d->lane0port); +						printf("        Lane 1 Port: %i\n", +								d->lane1port); +						printf("        Lane 2 Port: %i\n", +								d->lane2port); +						printf("        Lane 3 Port: %i\n\n", +								d->lane3port); +						offset += FRU_PICMGEXT_AMC_CHANNEL_DESC_RECORD_SIZE; +					} +					for (; offset < off + length;) { +						unsigned int data[2]; +						struct fru_picmgext_amc_link_desc_record *l; +						l = (struct fru_picmgext_amc_link_desc_record *)&data[0]; +						data[0] = fru_data[offset] | +							(fru_data[offset + 1] << 8) | +							(fru_data[offset + 2] << 16) | +							(fru_data[offset + 3] << 24); +						data[1] = fru_data[offset + 4]; +						printf( "      Link Designator:  Channel ID: %i\n" +								"            Port Flag 0: %s%s%s%s\n", +								l->channel_id, +								(l->port_flag_0)?"o":"-", +								(l->port_flag_1)?"o":"-", +								(l->port_flag_2)?"o":"-", +								(l->port_flag_3)?"o":"-"  ); +						switch (l->type) { +							case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE: +								/* AMC.1 */ +								printf( "        Link Type:       %02x - " +										"AMC.1 PCI Express\n", l->type); +								switch (l->type_ext) { +									case AMC_LINK_TYPE_EXT_PCIE_G1_NSSC: +										printf( "        Link Type Ext:   %i - " +												" Gen 1 capable - non SSC\n", +												l->type_ext); +									break; +									case AMC_LINK_TYPE_EXT_PCIE_G1_SSC: +										printf( "        Link Type Ext:   %i - " +												" Gen 1 capable - SSC\n", +												l->type_ext); +										break; +									case AMC_LINK_TYPE_EXT_PCIE_G2_NSSC: +										printf( "        Link Type Ext:   %i - " +												" Gen 2 capable - non SSC\n", +												l->type_ext); +										break; +									case AMC_LINK_TYPE_EXT_PCIE_G2_SSC: +										printf( "        Link Type Ext:   %i - " +												" Gen 2 capable - SSC\n", +												l->type_ext); +										break; +									default: +										printf( "        Link Type Ext:   %i - " +												" Invalid\n", +												l->type_ext); +										break; +								} +								break; + +							case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS1: +							case FRU_PICMGEXT_AMC_LINK_TYPE_PCIE_AS2: +								/* AMC.1 */ +								printf( "        Link Type:       %02x - " +										"AMC.1 PCI Express Advanced Switching\n", +										l->type); +								printf("        Link Type Ext:   %i\n", +										l->type_ext); +								break; + +							case FRU_PICMGEXT_AMC_LINK_TYPE_ETHERNET: +								/* AMC.2 */ +								printf( "        Link Type:       %02x - " +										"AMC.2 Ethernet\n", +										l->type); +								switch (l->type_ext) { +									case AMC_LINK_TYPE_EXT_ETH_1000_BX: +										printf( "        Link Type Ext:   %i - " +												" 1000Base-Bx (SerDES Gigabit) Ethernet Link\n", +												l->type_ext); +										break; + +									case AMC_LINK_TYPE_EXT_ETH_10G_XAUI: +										printf( "        Link Type Ext:   %i - " +												" 10Gbit XAUI Ethernet Link\n", +										l->type_ext); +										break; + +									default: +										printf( "        Link Type Ext:   %i - " +												" Invalid\n", +												l->type_ext); +										break; +								} +								break; + +							case FRU_PICMGEXT_AMC_LINK_TYPE_STORAGE: +								/* AMC.3 */ +								printf( "        Link Type:       %02x - " +										"AMC.3 Storage\n", +										l->type); +								switch (l->type_ext) { +									case AMC_LINK_TYPE_EXT_STORAGE_FC: +										printf( "        Link Type Ext:   %i - " +												" Fibre Channel\n", +												l->type_ext); +										break; + +									case AMC_LINK_TYPE_EXT_STORAGE_SATA: +										printf( "        Link Type Ext:   %i - " +												" Serial ATA\n", +												l->type_ext); +										break; + +									case AMC_LINK_TYPE_EXT_STORAGE_SAS: +										printf( "        Link Type Ext:   %i - " +												" Serial Attached SCSI\n", +												l->type_ext); +										break; + +									default: +										printf( "        Link Type Ext:   %i - " +												" Invalid\n", +												l->type_ext); +										break; +								} +								break; + +							case FRU_PICMGEXT_AMC_LINK_TYPE_RAPIDIO: +								/* AMC.4 */ +								printf( "        Link Type:       %02x - " +										"AMC.4 Serial Rapid IO\n", +										l->type); +								printf("        Link Type Ext:   %i\n", +										l->type_ext); +								break; + +							default: +								printf( "        Link Type:       %02x - " +										"reserved or OEM GUID", +										l->type); +								printf("        Link Type Ext:   %i\n", +										l->type_ext); +								break; +						} + +						printf("        Link group Id:   %i\n", +								l->group_id); +						printf("        Link Asym Match: %i\n\n", +								l->asym_match); +						offset += FRU_PICMGEXT_AMC_LINK_DESC_RECORD_SIZE; +					} +				} +			} +			break; + +		case FRU_AMC_CARRIER_INFO: +		{ +			unsigned char extVersion; +			unsigned char siteCount; + +			printf("    FRU_CARRIER_INFO\n"); + +			extVersion = fru_data[offset++]; +			siteCount  = fru_data[offset++]; + +			printf("      AMC.0 extension version: R%d.%d\n", +					(extVersion >> 0)& 0x0F, +					(extVersion >> 4)& 0x0F ); +			printf("      Carrier Sie Number Cnt: %d\n", siteCount); + +			for (i = 0 ; i < siteCount; i++ ){ +				printf("       Site ID: %i \n", fru_data[offset++]); +			} +			printf("\n"); +		} +		break; +		case FRU_PICMG_CLK_CARRIER_P2P: +		{ +			unsigned char desc_count; +			int i,j; + +			printf("    FRU_PICMG_CLK_CARRIER_P2P\n"); + +			desc_count = fru_data[offset++]; + +			for(i=0; i<desc_count; i++){ +				unsigned char resource_id; +				unsigned char channel_count; + +				resource_id   = fru_data[offset++]; +				channel_count = fru_data[offset++]; + +				printf("\n"); +				printf("      Clock Resource ID: 0x%02x  Type: ", resource_id); +				if((resource_id & 0xC0)>>6 == 0) {printf("On-Carrier-Device\n");} +				else if((resource_id & 0xC0)>>6 == 1) {printf("AMC slot\n");} +				else if((resource_id & 0xC0)>>6 == 2) {printf("Backplane\n");} +				else{ printf("reserved\n");} +				printf("      Channel Count: 0x%02x\n", channel_count); + +				for(j=0; j<channel_count; j++){ +					unsigned char loc_channel, rem_channel, rem_resource; + +					loc_channel  = fru_data[offset++]; +					rem_channel  = fru_data[offset++]; +					rem_resource = fru_data[offset++]; + +					printf("        CLK-ID: 0x%02x    ->", loc_channel); +					printf(" remote CLKID: 0x%02x   ", rem_channel); +					if((rem_resource & 0xC0)>>6 == 0) {printf("[ Carrier-Dev");} +					else if((rem_resource & 0xC0)>>6 == 1) {printf("[ AMC slot   ");} +					else if((rem_resource & 0xC0)>>6 == 2) {printf("[ Backplane  ");} +					else{ printf("reserved         ");} +					printf(" 0x%02x ]\n", rem_resource&0xF); +				} +			} +			printf("\n"); +		} +		break; +		case FRU_PICMG_CLK_CONFIG: +		{ +			unsigned char resource_id, descr_count; +			int i,j; + +			printf("    FRU_PICMG_CLK_CONFIG\n"); + +			resource_id = fru_data[offset++]; +			descr_count = fru_data[offset++]; + +			printf("\n"); +			printf("      Clock Resource ID: 0x%02x\n", resource_id); +			printf("      Descr. Count:      0x%02x\n", descr_count); + +			for(i=0; i<descr_count; i++){ +				unsigned char channel_id, control; +				unsigned char indirect_cnt, direct_cnt; + +				channel_id = fru_data[offset++]; +				control    = fru_data[offset++]; +				printf("        CLK-ID: 0x%02x  -  ", channel_id); +				printf("CTRL 0x%02x [ %12s ]\n", +									control, +									((control&0x1)==0)?"Carrier IPMC":"Application"); + +				indirect_cnt = fru_data[offset++]; +				direct_cnt   = fru_data[offset++]; +				printf("         Cnt: Indirect 0x%02x  /  Direct 0x%02x\n", +						indirect_cnt, +						direct_cnt); + +				/* indirect desc */ +				for(j=0; j<indirect_cnt; j++){ +					unsigned char feature; +					unsigned char dep_chn_id; + +					feature    = fru_data[offset++]; +					dep_chn_id = fru_data[offset++]; + +					printf("          Feature: 0x%02x [%8s] - ", feature, (feature&0x1)==1?"Source":"Receiver"); +					printf("          Dep. CLK-ID: 0x%02x\n", dep_chn_id); +				} + +				/* direct desc */ +				for(j=0; j<direct_cnt; j++){ +					unsigned char feature, family, accuracy; +					unsigned int freq, min_freq, max_freq; + +					feature  = fru_data[offset++]; +					family   = fru_data[offset++]; +					accuracy = fru_data[offset++]; +					freq     = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 ) +								| (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24); +					offset += 4; +					min_freq = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 ) +								| (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24); +					offset += 4; +					max_freq = (fru_data[offset+0] << 0 ) | (fru_data[offset+1] << 8 ) +								| (fru_data[offset+2] << 16) | (fru_data[offset+3] << 24); +					offset += 4; + +					printf("          - Feature: 0x%02x  - PLL: %x / Asym: %s\n", +							feature, +							(feature > 1) & 1, +							(feature&1)?"Source":"Receiver"); +					printf("            Family:  0x%02x  - AccLVL: 0x%02x\n", family, accuracy); +					printf("            FRQ: %-9ld - min: %-9ld - max: %-9ld\n", +							freq, min_freq, max_freq); +				} +				printf("\n"); +			} +			printf("\n"); +		} +		break; + +		case FRU_UTCA_FRU_INFO_TABLE: +		case FRU_UTCA_CARRIER_MNG_IP: +		case FRU_UTCA_CARRIER_INFO: +		case FRU_UTCA_CARRIER_LOCATION: +		case FRU_UTCA_SHMC_IP_LINK: +		case FRU_UTCA_POWER_POLICY: +		case FRU_UTCA_ACTIVATION: +		case FRU_UTCA_PM_CAPABILTY: +		case FRU_UTCA_FAN_GEOGRAPHY: +		case FRU_UTCA_CLOCK_MAPPING: +		case FRU_UTCA_MSG_BRIDGE_POLICY: +		case FRU_UTCA_OEM_MODULE_DESC: +			printf("    Not implemented yet. uTCA specific record found!!\n"); +			printf("     - Record ID: 0x%02x\n", h->record_id); +		break; + +		default: +			printf("    Unknown OEM Extension Record ID: %x\n", h->record_id); +		break; + +	} +} + + +/* __ipmi_fru_print  -  Do actual work to print a FRU by its ID +* +* @intf:   ipmi interface +* @id:     fru id +* +* returns -1 on error +* returns 0 if successful +* returns 1 if device not present +*/ +static int +__ipmi_fru_print(struct ipmi_intf * intf, uint8_t id) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	struct fru_info fru; +	struct fru_header header; +	uint8_t msg_data[4]; + +	memset(&fru, 0, sizeof(struct fru_info)); +	memset(&header, 0, sizeof(struct fru_header)); + +	/* +	* get info about this FRU +	*/ +	memset(msg_data, 0, 4); +	msg_data[0] = id; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_INFO; +	req.msg.data = msg_data; +	req.msg.data_len = 1; + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		printf(" Device not present (No Response)\n"); +		return -1; +	} +	if (rsp->ccode > 0) { +		printf(" Device not present (%s)\n", +			val2str(rsp->ccode, completion_code_vals)); +		return -1; +	} + +	memset(&fru, 0, sizeof(fru)); +	fru.size = (rsp->data[1] << 8) | rsp->data[0]; +	fru.access = rsp->data[2] & 0x1; + +	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)", +		fru.size, fru.access ? "words" : "bytes"); + +	if (fru.size < 1) { +		lprintf(LOG_ERR, " Invalid FRU size %d", fru.size); +		return -1; +	} + +	/* +	* retrieve the FRU header +	*/ +	msg_data[0] = id; +	msg_data[1] = 0; +	msg_data[2] = 0; +	msg_data[3] = 8; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_DATA; +	req.msg.data = msg_data; +	req.msg.data_len = 4; + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		printf(" Device not present (No Response)\n"); +		return 1; +	} +	if (rsp->ccode > 0) { +		printf(" Device not present (%s)\n", +				val2str(rsp->ccode, completion_code_vals)); +		return 1; +	} + +	if (verbose > 1) +		printbuf(rsp->data, rsp->data_len, "FRU DATA"); + +	memcpy(&header, rsp->data + 1, 8); + +	if (header.version != 1) { +		lprintf(LOG_ERR, " Unknown FRU header version 0x%02x", +			header.version); +		return -1; +	} + +	/* offsets need converted to bytes +	* but that conversion is not done to the structure +	* because we may end up with offset > 255 +	* which would overflow our 1-byte offset field */ + +	lprintf(LOG_DEBUG, "fru.header.version:         0x%x", +		header.version); +	lprintf(LOG_DEBUG, "fru.header.offset.internal: 0x%x", +		header.offset.internal * 8); +	lprintf(LOG_DEBUG, "fru.header.offset.chassis:  0x%x", +		header.offset.chassis * 8); +	lprintf(LOG_DEBUG, "fru.header.offset.board:    0x%x", +		header.offset.board * 8); +	lprintf(LOG_DEBUG, "fru.header.offset.product:  0x%x", +		header.offset.product * 8); +	lprintf(LOG_DEBUG, "fru.header.offset.multi:    0x%x", +		header.offset.multi * 8); + +	/* +	* rather than reading the entire part +	* only read the areas we'll format +	*/ +	/* chassis area */ +	if ((header.offset.chassis*8) >= sizeof(struct fru_header)) +		fru_area_print_chassis(intf, &fru, id, header.offset.chassis*8); + +	/* board area */ +	if ((header.offset.board*8) >= sizeof(struct fru_header)) +		fru_area_print_board(intf, &fru, id, header.offset.board*8); + +	/* product area */ +	if ((header.offset.product*8) >= sizeof(struct fru_header)) +		fru_area_print_product(intf, &fru, id, header.offset.product*8); + +	/* multirecord area */ +	if( verbose==0 ) /* scipp parsing multirecord */ +		return 0; + +	if ((header.offset.multi*8) >= sizeof(struct fru_header)) +		fru_area_print_multirec(intf, &fru, id, header.offset.multi*8); + +	return 0; +} + +/* ipmi_fru_print  -  Print a FRU from its SDR locator record +* +* @intf:   ipmi interface +* @fru: SDR FRU Locator Record +* +* returns -1 on error +*/ +int +ipmi_fru_print(struct ipmi_intf * intf, struct sdr_record_fru_locator * fru) +{ +	char desc[17]; +	uint8_t  bridged_request = 0; +	uint32_t save_addr; +	uint32_t save_channel; +	int rc = 0; + +	if (fru == NULL) +		return __ipmi_fru_print(intf, 0); + +	/* Logical FRU Device +	*  dev_type == 0x10 +	*  modifier +	*   0x00 = IPMI FRU Inventory +	*   0x01 = DIMM Memory ID +	*   0x02 = IPMI FRU Inventory +	*   0x03 = System Processor FRU +	*   0xff = unspecified +	* +	* EEPROM 24C01 or equivalent +	*  dev_type >= 0x08 && dev_type <= 0x0f +	*  modifier +	*   0x00 = unspecified +	*   0x01 = DIMM Memory ID +	*   0x02 = IPMI FRU Inventory +	*   0x03 = System Processor Cartridge +	*/ +	if (fru->dev_type != 0x10 && +		(fru->dev_type_modifier != 0x02 || +		fru->dev_type < 0x08 || fru->dev_type > 0x0f)) +		return -1; + +	if (fru->dev_slave_addr == IPMI_BMC_SLAVE_ADDR && +		fru->device_id == 0) +		return 0; + +	memset(desc, 0, sizeof(desc)); +	memcpy(desc, fru->id_string, fru->id_code & 0x01f); +	desc[fru->id_code & 0x01f] = 0; +	printf("FRU Device Description : %s (ID %d)\n", desc, fru->device_id); + +	switch (fru->dev_type_modifier) { +	case 0x00: +	case 0x02: +		if (BRIDGE_TO_SENSOR(intf, fru->dev_slave_addr, +					   fru->channel_num)) { +			bridged_request = 1; +			save_addr = intf->target_addr; +			intf->target_addr = fru->dev_slave_addr; +			save_channel = intf->target_channel; +			intf->target_channel = fru->channel_num; +		} +		/* print FRU */ +		rc = __ipmi_fru_print(intf, fru->device_id); +		if (bridged_request) { +			intf->target_addr = save_addr; +			intf->target_channel = save_channel; +		} +		break; +	case 0x01: +		rc = ipmi_spd_print_fru(intf, fru->device_id); +		break; +	default: +		if (verbose) +			printf(" Unsupported device 0x%02x " +					"type 0x%02x with modifier 0x%02x\n", +					fru->device_id, fru->dev_type, +					fru->dev_type_modifier); +		else +			printf(" Unsupported device\n"); +	} +	printf("\n"); + +	return rc; +} + +/* ipmi_fru_print_all  -  Print builtin FRU + SDR FRU Locator records +* +* @intf:   ipmi interface +* +* returns -1 on error +*/ +static int +ipmi_fru_print_all(struct ipmi_intf * intf) +{ +	struct ipmi_sdr_iterator * itr; +	struct sdr_get_rs * header; +	struct sdr_record_fru_locator * fru; +	int rc; +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	struct ipm_devid_rsp *devid; +	struct sdr_record_mc_locator * mc; +	uint32_t save_addr; + +	printf("FRU Device Description : Builtin FRU Device (ID 0)\n"); +	/* TODO: Figure out if FRU device 0 may show up in SDR records. */ + +	/* Do a Get Device ID command to determine device support */ +	memset (&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_APP; +	req.msg.cmd = BMC_GET_DEVICE_ID; +	req.msg.data_len = 0; + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Get Device ID command failed"); +		return -1; +	} +	if (rsp->ccode > 0) { +		lprintf(LOG_ERR, "Get Device ID command failed: %s", +			val2str(rsp->ccode, completion_code_vals)); +		return -1; +	} + +	devid = (struct ipm_devid_rsp *) rsp->data; + +	/* Check the FRU inventory device bit to decide whether various */ +	/* FRU commands can be issued to FRU device #0 LUN 0		*/ + +	if (devid->adtl_device_support & 0x08) {	/* FRU Inventory Device bit? */ +		rc = ipmi_fru_print(intf, NULL); +		printf("\n"); +	} + +	if ((itr = ipmi_sdr_start(intf, 0)) == NULL) +		return -1; + +	/* Walk the SDRs looking for FRU Devices and Management Controller Devices. */ +	/* For FRU devices, print the FRU from the SDR locator record.		    */ +	/* For MC devices, issue FRU commands to the satellite controller to print  */ +	/* FRU data.								    */ +	while ((header = ipmi_sdr_get_next_header(intf, itr)) != NULL) +	{ +		if (header->type == SDR_RECORD_TYPE_MC_DEVICE_LOCATOR ) { +			/* Check the capabilities of the Management Controller Device */ +			mc = (struct sdr_record_mc_locator *) +				ipmi_sdr_get_record(intf, header, itr); +			/* Does this MC device support FRU inventory device? */ +			if (mc && (mc->dev_support & 0x08)) {	 /* FRU inventory device? */ +				/* Yes. Prepare to issue FRU commands to FRU device #0 LUN 0  */ +				/* using the slave address specified in the MC record.	      */ + +				/* save current target address */ +				save_addr = intf->target_addr; + +				/* set new target address to satellite controller */ +				intf->target_addr = mc->dev_slave_addr; + +				printf("FRU Device Description : %-16s\n", mc->id_string); + +				/* print the FRU by issuing FRU commands to the satellite     */ +				/* controller.						      */ +				rc = __ipmi_fru_print(intf, 0); + +				printf("\n"); + +				/* restore previous target */ +				intf->target_addr = save_addr; +			} + +			if (mc) { +				free(mc); +				mc = NULL; +			} + +			continue; +		} + +		if (header->type != SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR) +			continue; + +		/* Print the FRU from the SDR locator record. */ +		fru = (struct sdr_record_fru_locator *) +			ipmi_sdr_get_record(intf, header, itr); +		if (fru == NULL || !fru->logical) { +			if (fru) { +				free(fru); +				fru = NULL; +			} +			continue; +		} +		rc = ipmi_fru_print(intf, fru); +		free(fru); +		fru = NULL; +	} + +	ipmi_sdr_end(intf, itr); + +	return rc; +} + +/* ipmi_fru_read_help() - print help text for 'read' + * + * returns void + */ +void +ipmi_fru_read_help() +{ +	lprintf(LOG_NOTICE, "fru read <fru id> <fru file>"); +	lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified."); +	lprintf(LOG_NOTICE, "Example: ipmitool fru read 0 /root/fru.bin"); +} /* ipmi_fru_read_help() */ + +static void +ipmi_fru_read_to_bin(struct ipmi_intf * intf, +			char * pFileName, +			uint8_t fruId) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	struct fru_info fru; +	uint8_t msg_data[4]; +	uint8_t * pFruBuf; + +	msg_data[0] = fruId; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_INFO; +	req.msg.data = msg_data; +	req.msg.data_len = 1; + +	rsp = intf->sendrecv(intf, &req); +	if (!rsp) +		return; + +	if (rsp->ccode > 0) { +		if (rsp->ccode == 0xc3) +			printf ("  Timeout accessing FRU info. (Device not present?)\n"); +		return; +	} + +	memset(&fru, 0, sizeof(fru)); +	fru.size = (rsp->data[1] << 8) | rsp->data[0]; +	fru.access = rsp->data[2] & 0x1; + +	if (verbose) { +		printf("Fru Size   = %d bytes\n",fru.size); +		printf("Fru Access = %xh\n", fru.access); +	} + +	pFruBuf = malloc(fru.size); +	if (pFruBuf != NULL) { +		printf("Fru Size         : %d bytes\n",fru.size); +		read_fru_area(intf, &fru, fruId, 0, fru.size, pFruBuf); +	} else { +		lprintf(LOG_ERR, "Cannot allocate %d bytes\n", fru.size); +		return; +	} + +	if(pFruBuf != NULL) +	{ +		FILE * pFile; +		pFile = fopen(pFileName,"wb"); +		if (pFile) { +			fwrite(pFruBuf, fru.size, 1, pFile); +			printf("Done\n"); +		} else { +			lprintf(LOG_ERR, "Error opening file %s\n", pFileName); +			free(pFruBuf); +			pFruBuf = NULL; +			return; +		} +		fclose(pFile); +	} +	free(pFruBuf); +	pFruBuf = NULL; +} + +static void +ipmi_fru_write_from_bin(struct ipmi_intf * intf, +			char * pFileName, +			uint8_t fruId) +{ +	struct ipmi_rs *rsp; +	struct ipmi_rq req; +	struct fru_info fru; +	uint8_t msg_data[4]; +	uint8_t *pFruBuf; +	uint16_t len = 0; +	FILE *pFile; + +	msg_data[0] = fruId; + +	memset(&req, 0, sizeof (req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_INFO; +	req.msg.data = msg_data; +	req.msg.data_len = 1; + +	rsp = intf->sendrecv(intf, &req); +	if (!rsp) +		return; + +	if (rsp->ccode) { +		if (rsp->ccode == 0xc3) +			printf("  Timeout accessing FRU info. (Device not present?)\n"); +		return; +	} + +	memset(&fru, 0, sizeof(fru)); +	fru.size = (rsp->data[1] << 8) | rsp->data[0]; +	fru.access = rsp->data[2] & 0x1; + +	if (verbose) { +		printf("Fru Size   = %d bytes\n", fru.size); +		printf("Fru Access = %xh\n", fru.access); +	} + +	pFruBuf = malloc(fru.size); +	if (pFruBuf == NULL) { +		lprintf(LOG_ERR, "Cannot allocate %d bytes\n", fru.size); +		return; +	} + +		pFile = fopen(pFileName, "rb"); +		if (pFile != NULL) { +			len = fread(pFruBuf, 1, fru.size, pFile); +			printf("Fru Size         : %d bytes\n", fru.size); +			printf("Size to Write    : %d bytes\n", len); +			fclose(pFile); +		} else { +		lprintf(LOG_ERR, "Error opening file %s\n", pFileName); +		} + +		if (len != 0) { +			write_fru_area(intf, &fru, fruId,0, 0, len, pFruBuf); +			lprintf(LOG_INFO,"Done"); +		} + +	free(pFruBuf); +	pFruBuf = NULL; +} + +/* ipmi_fru_write_help() - print help text for 'write' + * + * retruns void + */ +void +ipmi_fru_write_help() +{ +	lprintf(LOG_NOTICE, "fru write <fru id> <fru file>"); +	lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified."); +	lprintf(LOG_NOTICE, "Example: ipmitool fru write 0 /root/fru.bin"); +} /* ipmi_fru_write_help() */ + +/* ipmi_fru_edit_help - print help text for 'fru edit' command + * + * returns void + */ +void +ipmi_fru_edit_help() +{ +	lprintf(LOG_NOTICE, +			"fru edit <fruid> field <section> <index> <string> - edit FRU string"); +	lprintf(LOG_NOTICE, +			"fru edit <fruid> oem iana <record> <format> <args> - limited OEM support"); +} /* ipmi_fru_edit_help() */ + +/* ipmi_fru_edit_multirec  -  Query new values to replace original FRU content +* +* @intf:   interface to use +* @id:  FRU id to work on +* +* returns: nothing +*/ +/* Work in progress, copy paste most of the stuff for other functions in this +	file ... not elegant yet */ +static int +ipmi_fru_edit_multirec(struct ipmi_intf * intf, uint8_t id , +												int argc, char ** argv) +{ + +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	struct fru_info fru; +	struct fru_header header; +	uint8_t msg_data[4]; + +	uint16_t retStatus = 0; +	uint32_t offFruMultiRec; +	uint32_t fruMultiRecSize = 0; +	struct fru_info fruInfo; +	retStatus = ipmi_fru_get_multirec_location_from_fru(intf, id, &fruInfo, +								&offFruMultiRec, +								&fruMultiRecSize); + + +	lprintf(LOG_DEBUG, "FRU Size        : %lu\n", fruMultiRecSize); +	lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec); + +	{ + + +	memset(&fru, 0, sizeof(struct fru_info)); +	memset(&header, 0, sizeof(struct fru_header)); + +	/* +	* get info about this FRU +	*/ +	memset(msg_data, 0, 4); +	msg_data[0] = id; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_INFO; +	req.msg.data = msg_data; +	req.msg.data_len = 1; + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		printf(" Device not present (No Response)\n"); +		return -1; +	} +	if (rsp->ccode > 0) { +		printf(" Device not present (%s)\n", +			val2str(rsp->ccode, completion_code_vals)); +		return -1; +	} + +	memset(&fru, 0, sizeof(fru)); +	fru.size = (rsp->data[1] << 8) | rsp->data[0]; +	fru.access = rsp->data[2] & 0x1; + +	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)", +		fru.size, fru.access ? "words" : "bytes"); + +	if (fru.size < 1) { +		lprintf(LOG_ERR, " Invalid FRU size %d", fru.size); +		return -1; +	} +	} + +	{ +		uint8_t * fru_data; +		uint32_t fru_len, i; +		uint32_t offset= offFruMultiRec; +		struct fru_multirec_header * h; +		uint32_t last_off, len; +		uint8_t error=0; + +		i = last_off = offset; +		fru_len = 0; + +		memset(&fru, 0, sizeof(fru)); +		fru_data = malloc(fru.size + 1); +		if (fru_data == NULL) { +			lprintf(LOG_ERR, " Out of memory!"); +			return -1; +		} +		memset(fru_data, 0, fru.size + 1); + +		do { +			h = (struct fru_multirec_header *) (fru_data + i); + +			/* read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time */ +			if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len))) +			{ +				len = fru.size - last_off; +				if (len > FRU_MULTIREC_CHUNK_SIZE) +					len = FRU_MULTIREC_CHUNK_SIZE; + +				if (read_fru_area(intf, &fru, id, last_off, len, fru_data) < 0) +					break; + +				last_off += len; +			} +			if( h->type ==  FRU_RECORD_TYPE_OEM_EXTENSION ){ + +				struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *) +										&fru_data[i + sizeof(struct fru_multirec_header)]; +				uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16; + +				uint32_t suppliedIana = 0 ; +				/* Now makes sure this is really PICMG record */ + +				/* Default to PICMG for backward compatibility */ +				if( argc <=2 ) { +					suppliedIana =  IPMI_OEM_PICMG; +				}  else { +					if( !strncmp( argv[2] , "oem" , 3 )) { +						/* Expect IANA number next */ +						if( argc <= 3 ) { +							lprintf(LOG_ERR, "oem iana <record> <format> [<args>]"); +							error = 1; +						} else { +							if (str2uint(argv[3], &suppliedIana) == 0) { +								lprintf(LOG_DEBUG, +										"using iana: %d", +										suppliedIana); +							} else { +								lprintf(LOG_ERR, +										"Given IANA '%s' is invalid.", +										argv[3]); +								error = 1; +							} +						} +					} +				} + +				if( suppliedIana == iana ) { +					lprintf(LOG_DEBUG, "Matching record found" ); + +					if( iana == IPMI_OEM_PICMG ){ +						if( ipmi_fru_picmg_ext_edit(fru_data, +						i + sizeof(struct fru_multirec_header), +						h->len, h, oh )){ +							/* The fru changed */ +							write_fru_area(intf,&fru,id, i,i, +						h->len+ sizeof(struct fru_multirec_header), fru_data); +						} +					} +					else if( iana == IPMI_OEM_KONTRON ) { +						if( ipmi_fru_oemkontron_edit( argc,argv,fru_data, +						i + sizeof(struct fru_multirec_header), +						h->len, h, oh )){ +							/* The fru changed */ +							write_fru_area(intf,&fru,id, i,i, +						h->len+ sizeof(struct fru_multirec_header), fru_data); +						} +					} +					/* FIXME: Add OEM record support here */ +					else{ +						printf("  OEM IANA (%s) Record not support in this mode\n", +															val2str( iana,  ipmi_oem_info)); +						error = 1; +					} +				} +			} +			i += h->len + sizeof (struct fru_multirec_header); +		} while (!(h->format & 0x80) && (error != 1)); + +		free(fru_data); +		fru_data = NULL; +	} +	return 0; +} + +/* ipmi_fru_get_help - print help text for 'fru get' + * + * returns void + */ +void +ipmi_fru_get_help() +{ +	lprintf(LOG_NOTICE, +			"fru get <fruid> oem iana <record> <format> <args> - limited OEM support"); +} /* ipmi_fru_get_help() */ + +void +ipmi_fru_internaluse_help() +{ +	lprintf(LOG_NOTICE, +			"fru internaluse <fru id> info             - get internal use area size"); +	lprintf(LOG_NOTICE, +			"fru internaluse <fru id> print            - print internal use area in hex"); +	lprintf(LOG_NOTICE, +			"fru internaluse <fru id> read  <fru file> - read internal use area to file"); +	lprintf(LOG_NOTICE, +			"fru internaluse <fru id> write <fru file> - write internal use area from file"); +} /* void ipmi_fru_internaluse_help() */ + +/* ipmi_fru_get_multirec   -  Query new values to replace original FRU content +* +* @intf:   interface to use +* @id:  FRU id to work on +* +* returns: nothing +*/ +/* Work in progress, copy paste most of the stuff for other functions in this +	file ... not elegant yet */ +static int +ipmi_fru_get_multirec(struct ipmi_intf * intf, uint8_t id , +												int argc, char ** argv) +{ + +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	struct fru_info fru; +	struct fru_header header; +	uint8_t msg_data[4]; + +	uint16_t retStatus = 0; +	uint32_t offFruMultiRec; +	uint32_t fruMultiRecSize = 0; +	struct fru_info fruInfo; +	retStatus = ipmi_fru_get_multirec_location_from_fru(intf, id, &fruInfo, +								&offFruMultiRec, +								&fruMultiRecSize); + + +	lprintf(LOG_DEBUG, "FRU Size        : %lu\n", fruMultiRecSize); +	lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec); + +	{ + + +	memset(&fru, 0, sizeof(struct fru_info)); +	memset(&header, 0, sizeof(struct fru_header)); + +	/* +	* get info about this FRU +	*/ +	memset(msg_data, 0, 4); +	msg_data[0] = id; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_INFO; +	req.msg.data = msg_data; +	req.msg.data_len = 1; + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		printf(" Device not present (No Response)\n"); +		return -1; +	} +	if (rsp->ccode > 0) { +		printf(" Device not present (%s)\n", +			val2str(rsp->ccode, completion_code_vals)); +		return -1; +	} + +	memset(&fru, 0, sizeof(fru)); +	fru.size = (rsp->data[1] << 8) | rsp->data[0]; +	fru.access = rsp->data[2] & 0x1; + +	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)", +		fru.size, fru.access ? "words" : "bytes"); + +	if (fru.size < 1) { +		lprintf(LOG_ERR, " Invalid FRU size %d", fru.size); +		return -1; +	} +	} + +	{ +		uint8_t * fru_data; +		uint32_t fru_len, i; +		uint32_t offset= offFruMultiRec; +		struct fru_multirec_header * h; +		uint32_t last_off, len; +		uint8_t error=0; + +		i = last_off = offset; +		fru_len = 0; + +		fru_data = malloc(fru.size + 1); +		if (fru_data == NULL) { +			lprintf(LOG_ERR, " Out of memory!"); +			return -1; +		} +		memset(fru_data, 0, fru.size + 1); + +		do { +			h = (struct fru_multirec_header *) (fru_data + i); + +			/* read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time */ +			if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len))) +			{ +				len = fru.size - last_off; +				if (len > FRU_MULTIREC_CHUNK_SIZE) +					len = FRU_MULTIREC_CHUNK_SIZE; + +				if (read_fru_area(intf, &fru, id, last_off, len, fru_data) < 0) +					break; + +				last_off += len; +			} +			if( h->type ==  FRU_RECORD_TYPE_OEM_EXTENSION ){ + +				struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *) +										&fru_data[i + sizeof(struct fru_multirec_header)]; +				uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16; + +				uint32_t suppliedIana = 0 ; +				/* Now makes sure this is really PICMG record */ +				if( !strncmp( argv[2] , "oem" , 3 )) { +					/* Expect IANA number next */ +					if( argc <= 3 ) { +						lprintf(LOG_ERR, "oem iana <record> <format>"); +						error = 1; +					} else { +						if (str2uint(argv[3], &suppliedIana) == 0) { +							lprintf(LOG_DEBUG, +									"using iana: %d", +									suppliedIana); +						} else { +							lprintf(LOG_ERR, +									"Given IANA '%s' is invalid.", +									argv[3]); +							error = 1; +						} +					} +				} + +				if( suppliedIana == iana ) { +					lprintf(LOG_DEBUG, "Matching record found" ); + +				if( iana == IPMI_OEM_KONTRON ) { +						ipmi_fru_oemkontron_get( argc,argv,fru_data, +						i + sizeof(struct fru_multirec_header), +						h->len, h, oh ); +					} +					/* FIXME: Add OEM record support here */ +					else{ +						printf("  OEM IANA (%s) Record not supported in this mode\n", +															val2str( iana,  ipmi_oem_info)); +						error = 1; +					} +				} +			} +			i += h->len + sizeof (struct fru_multirec_header); +		} while (!(h->format & 0x80) && (error != 1)); + +		free(fru_data); +		fru_data = NULL; +	} +	return 0; +} + +static int +ipmi_fru_upg_ekeying(struct ipmi_intf * intf, +			char * pFileName, +			uint8_t fruId) +{ +	struct fru_info fruInfo; +	uint8_t *buf = NULL; +	uint32_t offFruMultiRec = 0; +	uint32_t fruMultiRecSize = 0; +	uint32_t offFileMultiRec = 0; +	uint32_t fileMultiRecSize = 0; +	if (pFileName == NULL) { +		lprintf(LOG_ERR, "File expected, but none given."); +		return (-1); +	} +	if (ipmi_fru_get_multirec_location_from_fru(intf, fruId, &fruInfo, +							&offFruMultiRec, &fruMultiRecSize) != 0) { +		lprintf(LOG_ERR, "Failed to get multirec location from FRU."); +		return (-1); +	} +	lprintf(LOG_DEBUG, "FRU Size        : %lu\n", fruMultiRecSize); +	lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec); +	if (ipmi_fru_get_multirec_size_from_file(pFileName, &fileMultiRecSize, +				&offFileMultiRec) != 0) { +		lprintf(LOG_ERR, "Failed to get multirec size from file '%s'.", pFileName); +		return (-1); +	} +	buf = malloc(fileMultiRecSize); +	if (buf == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return (-1); +	} +	if (ipmi_fru_get_multirec_from_file(pFileName, buf, fileMultiRecSize, +				offFileMultiRec) != 0) { +		lprintf(LOG_ERR, "Failed to get multirec from file '%s'.", pFileName); +		if (buf != NULL) { +			free(buf); +			buf = NULL; +		} +		return (-1); +	} +	if (ipmi_fru_get_adjust_size_from_buffer(buf, &fileMultiRecSize) != 0) { +		lprintf(LOG_ERR, "Failed to adjust size from buffer."); +		if (buf != NULL) { +			free(buf); +			buf = NULL; +		} +		return (-1); +	} +	if (write_fru_area(intf, &fruInfo, fruId, 0, offFruMultiRec, +				fileMultiRecSize, buf) != 0) { +		lprintf(LOG_ERR, "Failed to write FRU area."); +		if (buf != NULL) { +			free(buf); +			buf = NULL; +		} +		return (-1); +	} +	if (buf != NULL) { +		free(buf); +		buf = NULL; +	} +	lprintf(LOG_INFO, "Done upgrading Ekey."); +	return 0; +} + +/* ipmi_fru_upgekey_help - print help text for 'upgEkey' + * + * returns void + */ +void +ipmi_fru_upgekey_help() +{ +	lprintf(LOG_NOTICE, "fru upgEkey <fru id> <fru file>"); +	lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified."); +	lprintf(LOG_NOTICE, "Example: ipmitool fru upgEkey 0 /root/fru.bin"); +} /* ipmi_fru_upgekey_help() */ + +static int +ipmi_fru_get_multirec_size_from_file(char * pFileName, +					uint32_t * pSize, +					uint32_t * pOffset) +{ +	struct fru_header header; +	FILE * pFile; +	uint8_t len = 0; +	uint32_t end = 0; +	*pSize = 0; + +	pFile = fopen(pFileName,"rb"); +	if (pFile) { +		rewind(pFile); +		len = fread(&header, 1, 8, pFile); +		fseek(pFile, 0, SEEK_END); +		end = ftell(pFile); +		fclose(pFile); +	} + +	lprintf(LOG_DEBUG, "File Size = %lu\n", end); +	lprintf(LOG_DEBUG, "Len = %u\n", len); + +	if (len != 8) { +		printf("Error with file %s in getting size\n", pFileName); +		return -1; +	} + +	if (header.version != 0x01) { +		printf ("Unknown FRU header version %02x.\n", header.version); +		return -1; +	} + +	/* Retreive length */ +	if (((header.offset.internal * 8) > (header.offset.internal * 8)) && +		((header.offset.internal * 8) < end)) +		end = (header.offset.internal * 8); + +	if (((header.offset.chassis * 8) > (header.offset.chassis * 8)) && +		((header.offset.chassis * 8) < end)) +		end = (header.offset.chassis * 8); + +	if (((header.offset.board * 8) > (header.offset.board * 8)) && +		((header.offset.board * 8) < end)) +		end = (header.offset.board * 8); + +	if (((header.offset.product * 8) > (header.offset.product * 8)) && +		((header.offset.product * 8) < end)) +		end = (header.offset.product * 8); + +	*pSize = end - (header.offset.multi * 8); +	*pOffset = (header.offset.multi * 8); + +	return 0; +} + +int +ipmi_fru_get_adjust_size_from_buffer(uint8_t * fru_data, uint32_t *pSize) +{ +	struct fru_multirec_header * head; +	int status = 0; +	uint8_t checksum = 0; +	uint8_t counter = 0; +	uint16_t count = 0; +	do { +		checksum = 0; +		head = (struct fru_multirec_header *) (fru_data + count); +		if (verbose) { +			printf("Adding ("); +		} +		for (counter = 0; counter < sizeof(struct fru_multirec_header); counter++) { +			if (verbose) { +				printf(" %02X", *(fru_data + count + counter)); +			} +			checksum += *(fru_data + count + counter); +		} +		if (verbose) { +			printf(")"); +		} +		if (checksum != 0) { +			lprintf(LOG_ERR, "Bad checksum in Multi Records"); +			status = (-1); +			if (verbose) { +				printf("--> FAIL"); +			} +		} else if (verbose) { +			printf("--> OK"); +		} +		if (verbose > 1 && checksum == 0) { +			for (counter = 0; counter < head->len; counter++) { +				printf(" %02X", *(fru_data + count + counter +							+ sizeof(struct fru_multirec_header))); +			} +		} +		if (verbose) { +			printf("\n"); +		} +		count += head->len + sizeof (struct fru_multirec_header); +	} while ((!(head->format & 0x80)) && (status == 0)); + +	*pSize = count; +	lprintf(LOG_DEBUG, "Size of multirec: %lu\n", *pSize); +	return status; +} + +static int +ipmi_fru_get_multirec_from_file(char * pFileName, uint8_t * pBufArea, +		uint32_t size, uint32_t offset) +{ +	FILE * pFile; +	uint32_t len = 0; +	if (pFileName == NULL) { +		lprintf(LOG_ERR, "Invalid file name given."); +		return (-1); +	} +	 +	errno = 0; +	pFile = fopen(pFileName, "rb"); +	if (!pFile) { +		lprintf(LOG_ERR, "Error opening file '%s': %i -> %s.", pFileName, errno, +				strerror(errno)); +		return (-1); +	} +	errno = 0; +	if (fseek(pFile, offset, SEEK_SET) != 0) { +		lprintf(LOG_ERR, "Failed to seek in file '%s': %i -> %s.", pFileName, errno, +				strerror(errno)); +		fclose(pFile); +		return (-1); +	} +	len = fread(pBufArea, size, 1, pFile); +	fclose(pFile); + +	if (len != 1) { +		lprintf(LOG_ERR, "Error in file '%s'.", pFileName); +		return (-1); +	} +	return 0; +} + +static int +ipmi_fru_get_multirec_location_from_fru(struct ipmi_intf * intf, +					uint8_t fruId, +												struct fru_info *pFruInfo, +					uint32_t * pRetLocation, +					uint32_t * pRetSize) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	uint8_t msg_data[4]; +	uint32_t end; +	struct fru_header header; + +	*pRetLocation = 0; + +	msg_data[0] = fruId; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_INFO; +	req.msg.data = msg_data; +	req.msg.data_len = 1; + +	rsp = intf->sendrecv(intf, &req); +	if (!rsp) { +		if (verbose > 1) +			printf("no response\n"); +		return -1; +	} + +	if (rsp->ccode > 0) { +		if (rsp->ccode == 0xc3) +			printf ("  Timeout accessing FRU info. (Device not present?)\n"); +		else +			printf ("   CCODE = 0x%02x\n", rsp->ccode); +		return -1; +	} +	pFruInfo->size = (rsp->data[1] << 8) | rsp->data[0]; +	pFruInfo->access = rsp->data[2] & 0x1; + +	if (verbose > 1) +		printf("pFruInfo->size = %d bytes (accessed by %s)\n", +				pFruInfo->size, pFruInfo->access ? "words" : "bytes"); + +	if (!pFruInfo->size) +		return -1; + +	msg_data[0] = fruId; +	msg_data[1] = 0; +	msg_data[2] = 0; +	msg_data[3] = 8; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_DATA; +	req.msg.data = msg_data; +	req.msg.data_len = 4; + +	rsp = intf->sendrecv(intf, &req); + +	if (!rsp) +		return -1; +	if (rsp->ccode > 0) { +		if (rsp->ccode == 0xc3) +			printf ("  Timeout while reading FRU data. (Device not present?)\n"); +		return -1; +	} + +	if (verbose > 1) +		printbuf(rsp->data, rsp->data_len, "FRU DATA"); + +	memcpy(&header, rsp->data + 1, 8); + +	if (header.version != 0x01) { +		printf ("  Unknown FRU header version %02x.\n", header.version); +		return -1; +	} + +	end = pFruInfo->size; + +	/* Retreive length */ +	if (((header.offset.internal * 8) > (header.offset.internal * 8)) && +		((header.offset.internal * 8) < end)) +		end = (header.offset.internal * 8); + +	if (((header.offset.chassis * 8) > (header.offset.chassis * 8)) && +		((header.offset.chassis * 8) < end)) +		end = (header.offset.chassis * 8); + +	if (((header.offset.board * 8) > (header.offset.board * 8)) && +		((header.offset.board * 8) < end)) +		end = (header.offset.board * 8); + +	if (((header.offset.product * 8) > (header.offset.product * 8)) && +		((header.offset.product * 8) < end)) +		end = (header.offset.product * 8); + +	*pRetSize = end; +	*pRetLocation = 8 * header.offset.multi; + +	return 0; +} + +/* ipmi_fru_get_internal_use_offset -  Retreive internal use offset +* +* @intf:   ipmi interface +* @id:     fru id +* +* returns -1 on error +* returns 0 if successful +* returns 1 if device not present +*/ +static int +ipmi_fru_get_internal_use_info(  struct ipmi_intf * intf, +											uint8_t id, +											struct fru_info * fru, +											uint16_t * size, +											uint16_t * offset) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	struct fru_header header; +	uint8_t msg_data[4]; + +	// Init output value +	* offset = 0; +	* size = 0; + +	memset(fru, 0, sizeof(struct fru_info)); +	memset(&header, 0, sizeof(struct fru_header)); + +	/* +	* get info about this FRU +	*/ +	memset(msg_data, 0, 4); +	msg_data[0] = id; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_INFO; +	req.msg.data = msg_data; +	req.msg.data_len = 1; + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		printf(" Device not present (No Response)\n"); +		return -1; +	} +	if (rsp->ccode > 0) { +		printf(" Device not present (%s)\n", +			val2str(rsp->ccode, completion_code_vals)); +		return -1; +	} + +	memset(&fru, 0, sizeof(fru)); +	fru->size = (rsp->data[1] << 8) | rsp->data[0]; +	fru->access = rsp->data[2] & 0x1; + +	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)", +		fru->size, fru->access ? "words" : "bytes"); + +	if (fru->size < 1) { +		lprintf(LOG_ERR, " Invalid FRU size %d", fru->size); +		return -1; +	} + +	/* +	* retrieve the FRU header +	*/ +	msg_data[0] = id; +	msg_data[1] = 0; +	msg_data[2] = 0; +	msg_data[3] = 8; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_DATA; +	req.msg.data = msg_data; +	req.msg.data_len = 4; + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		printf(" Device not present (No Response)\n"); +		return 1; +	} +	if (rsp->ccode > 0) { +		printf(" Device not present (%s)\n", +				val2str(rsp->ccode, completion_code_vals)); +		return 1; +	} + +	if (verbose > 1) +		printbuf(rsp->data, rsp->data_len, "FRU DATA"); + +	memcpy(&header, rsp->data + 1, 8); + +	if (header.version != 1) { +		lprintf(LOG_ERR, " Unknown FRU header version 0x%02x", +			header.version); +		return -1; +	} + +	lprintf(LOG_DEBUG, "fru.header.version:         0x%x", +		header.version); +	lprintf(LOG_DEBUG, "fru.header.offset.internal: 0x%x", +		header.offset.internal * 8); +	lprintf(LOG_DEBUG, "fru.header.offset.chassis:  0x%x", +		header.offset.chassis * 8); +	lprintf(LOG_DEBUG, "fru.header.offset.board:    0x%x", +		header.offset.board * 8); +	lprintf(LOG_DEBUG, "fru.header.offset.product:  0x%x", +		header.offset.product * 8); +	lprintf(LOG_DEBUG, "fru.header.offset.multi:    0x%x", +		header.offset.multi * 8); + +	if((header.offset.internal*8) == 0) +	{ +		* size = 0; +		* offset = 0; +	} +	else +	{ +		(* offset) = (header.offset.internal*8); + +		if(header.offset.chassis != 0) +		{ +			(* size) = ((header.offset.chassis*8)-(* offset)); +		} +		else if(header.offset.board != 0) +		{ +			(* size) = ((header.offset.board*8)-(* offset)); +		} +		else if(header.offset.product != 0) +		{ +			(* size) = ((header.offset.product*8)-(* offset)); +		} +		else if(header.offset.multi != 0) +		{ +			(* size) = ((header.offset.multi*8)-(* offset)); +		} +		else +		{ +			(* size) = (fru->size - (* offset)); +		} +	} +	return 0; +} + +/* ipmi_fru_info_internal_use -  print internal use info +* +* @intf:   ipmi interface +* @id:     fru id +* +* returns -1 on error +* returns 0 if successful +* returns 1 if device not present +*/ +static int +ipmi_fru_info_internal_use(struct ipmi_intf * intf, uint8_t id) +{ +	struct fru_info fru; +	uint16_t size; +	uint16_t offset; +	int rc = 0; + +	rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset); + +	if(rc == 0) +	{ +		lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset); +		printf(          "Internal Use Area Size  : %i\n", size); +	} +	else +	{ +		lprintf(LOG_ERR, "Cannot access internal use area"); +		return -1; +	} +	return 0; +} + +/* ipmi_fru_help - print help text for FRU subcommand + * + * returns void + */ +void +ipmi_fru_help() +{ +	lprintf(LOG_NOTICE, +			"FRU Commands:  print read write upgEkey edit internaluse get"); +} /* ipmi_fru_help() */ + +/* ipmi_fru_read_internal_use -  print internal use are in hex or file +* +* @intf:   ipmi interface +* @id:     fru id +* +* returns -1 on error +* returns 0 if successful +* returns 1 if device not present +*/ +static int +ipmi_fru_read_internal_use(struct ipmi_intf * intf, uint8_t id, char * pFileName) +{ +	struct fru_info fru; +	uint16_t size; +	uint16_t offset; +	int rc = 0; + +	rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset); + +	if(rc == 0) +	{ +		uint8_t * frubuf; + +		lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset); +		printf(          "Internal Use Area Size  : %i\n", size); + +		frubuf = malloc( size ); +		if(frubuf) +		{ +			rc = read_fru_area_section(intf, &fru, id, offset, size, frubuf); + +			if(rc == 0) +			{ +				if(pFileName == NULL) +				{ +					uint16_t counter; +					for(counter = 0; counter < size; counter ++) +					{ +						if((counter % 16) == 0) +							printf("\n%02i- ", (counter / 16)); +						printf("%02X ", frubuf[counter]); +					} +				} +				else +				{ +					FILE * pFile; +					pFile = fopen(pFileName,"wb"); +					if (pFile) +					{ +						fwrite(frubuf, size, 1, pFile); +						printf("Done\n"); +					} +					else +					{ +						lprintf(LOG_ERR, "Error opening file %s\n", pFileName); +						free(frubuf); +						frubuf = NULL; +						return -1; +					} +					fclose(pFile); +				} +			} +			printf("\n"); + +			free(frubuf); +			frubuf = NULL; +		} + +	} +	else +	{ +		lprintf(LOG_ERR, "Cannot access internal use area"); +	} +	return 0; +} + +/* ipmi_fru_write_internal_use   -  print internal use are in hex or file +* +* @intf:   ipmi interface +* @id:     fru id +* +* returns -1 on error +* returns 0 if successful +* returns 1 if device not present +*/ +static int +ipmi_fru_write_internal_use(struct ipmi_intf * intf, uint8_t id, char * pFileName) +{ +	struct fru_info fru; +	uint16_t size; +	uint16_t offset; +	int rc = 0; + +	rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset); + +	if(rc == 0) +	{ +		uint8_t * frubuf; +		FILE * fp; +		uint32_t fileLength = 0; + +		lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset); +		printf(            "Internal Use Area Size  : %i\n", size); + +		fp = fopen(pFileName, "r"); + +		if(fp) +		{ +			/* Retreive file length, check if it's fits the Eeprom Size */ +			fseek(fp, 0 ,SEEK_END); +			fileLength = ftell(fp); + +			lprintf(LOG_ERR, "File Size: %i", fileLength); +			lprintf(LOG_ERR, "Area Size: %i", size); +			if(fileLength != size) +			{ +				lprintf(LOG_ERR, "File size does not fit Eeprom Size"); +				fclose(fp); +				fp = NULL; +			} +			else +			{ +				fseek(fp, 0 ,SEEK_SET); +			} +		} + +		if(fp) +		{ +			frubuf = malloc( size ); +			if(frubuf) +			{ +				uint16_t fru_read_size; +				fru_read_size = fread(frubuf, 1, size, fp); + +				if(fru_read_size == size) +				{ +					rc = write_fru_area(intf, &fru, id, 0, offset, size, frubuf); + +					if(rc == 0) +					{ +						lprintf(LOG_INFO, "Done\n"); +					} +				} +				else +				{ +					lprintf(LOG_ERR, "Unable to read file: %i\n", fru_read_size); +				} + +				free(frubuf); +				frubuf = NULL; +			} +			fclose(fp); +			fp = NULL; +		} +	} +	else +	{ +		lprintf(LOG_ERR, "Cannot access internal use area"); +	} +	return 0; +} + +int +ipmi_fru_main(struct ipmi_intf * intf, int argc, char ** argv) +{ +	int rc = 0; +	uint8_t fru_id = 0; + +	if (argc < 1) { +		rc = ipmi_fru_print_all(intf); +	} +	else if (strncmp(argv[0], "help", 4) == 0) { +		ipmi_fru_help(); +		return 0; +	} +	else if (strncmp(argv[0], "print", 5) == 0 || +		strncmp(argv[0], "list", 4) == 0) { +		if (argc > 1) { +			if (strcmp(argv[1], "help") == 0) { +				lprintf(LOG_NOTICE, "fru print [fru id] - print information about FRU(s)"); +				return 0; +			} + +			if (is_fru_id(argv[1], &fru_id) != 0) +				return (-1); + +			rc = __ipmi_fru_print(intf, fru_id); +		} else { +			rc = ipmi_fru_print_all(intf); +		} +	} +	else if (!strncmp(argv[0], "read", 5)) { +		if (argc > 1 && strcmp(argv[1], "help") == 0) { +			ipmi_fru_read_help(); +			return 0; +		} else if (argc < 3) { +			lprintf(LOG_ERR, "Not enough parameters given."); +			ipmi_fru_read_help(); +			return (-1); +		} + +		if (is_fru_id(argv[1], &fru_id) != 0) +			return (-1); + +		/* There is a file name in the parameters */ +		if (is_valid_filename(argv[2]) != 0) +				return (-1); + +		if (verbose) { +			printf("FRU ID           : %d\n", fru_id); +			printf("FRU File         : %s\n", argv[2]); +		} +		/* TODO - rc is missing */ +		ipmi_fru_read_to_bin(intf, argv[2], fru_id); +	} +	else if (!strncmp(argv[0], "write", 5)) { +		if (argc > 1 && strcmp(argv[1], "help") == 0) { +			ipmi_fru_write_help(); +			return 0; +		} else if (argc < 3) { +			lprintf(LOG_ERR, "Not enough parameters given."); +			ipmi_fru_write_help(); +			return (-1); +		} + +		if (is_fru_id(argv[1], &fru_id) != 0) +			return (-1); + +		/* There is a file name in the parameters */ +		if (is_valid_filename(argv[2]) != 0) +				return (-1); + +		if (verbose) { +			printf("FRU ID           : %d\n", fru_id); +			printf("FRU File         : %s\n", argv[2]); +		} +		/* TODO - rc is missing */ +		ipmi_fru_write_from_bin(intf, argv[2], fru_id); +	} +	else if (!strncmp(argv[0], "upgEkey", 7)) { +		if (argc > 1 && strcmp(argv[1], "help") == 0) { +			ipmi_fru_upgekey_help(); +			return 0; +		} else if (argc < 3) { +			lprintf(LOG_ERR, "Not enough parameters given."); +			ipmi_fru_upgekey_help(); +			return (-1); +		} + +		if (is_fru_id(argv[1], &fru_id) != 0) +			return (-1); + +		/* There is a file name in the parameters */ +		if (is_valid_filename(argv[2]) != 0) +				return (-1); + +		rc = ipmi_fru_upg_ekeying(intf, argv[2], fru_id); +	} +	else if (!strncmp(argv[0], "internaluse", 11)) { +		if (argc > 1 && strcmp(argv[1], "help") == 0) { +			ipmi_fru_internaluse_help(); +			return 0; +		} + +		if ( (argc >= 3) && (!strncmp(argv[2], "info", 4)) ) { + +			if (is_fru_id(argv[1], &fru_id) != 0) +				return (-1); + +			rc = ipmi_fru_info_internal_use(intf, fru_id); +		} +		else if ( (argc >= 3) && (!strncmp(argv[2], "print", 5)) ) { + +			if (is_fru_id(argv[1], &fru_id) != 0) +				return (-1); + +			rc = ipmi_fru_read_internal_use(intf, fru_id, NULL); +		} +		else if ( (argc >= 4) && (!strncmp(argv[2], "read", 4)) ) { + +			if (is_fru_id(argv[1], &fru_id) != 0) +				return (-1); + +			/* There is a file name in the parameters */ +			if (is_valid_filename(argv[3]) != 0) +					return (-1); + +			lprintf(LOG_DEBUG, "FRU ID           : %d", fru_id); +			lprintf(LOG_DEBUG, "FRU File         : %s", argv[3]); + +			rc = ipmi_fru_read_internal_use(intf, fru_id, argv[3]); +		} +		else if ( (argc >= 4) && (!strncmp(argv[2], "write", 5)) ) { + +			if (is_fru_id(argv[1], &fru_id) != 0) +				return (-1); + +			/* There is a file name in the parameters */ +			if (is_valid_filename(argv[3]) != 0) +					return (-1); + +			lprintf(LOG_DEBUG, "FRU ID           : %d", fru_id); +			lprintf(LOG_DEBUG, "FRU File         : %s", argv[3]); + +			rc = ipmi_fru_write_internal_use(intf, fru_id, argv[3]); +		} else { +			lprintf(LOG_ERR, +					"Either unknown command or not enough parameters given."); +			ipmi_fru_internaluse_help(); +			return (-1); +		} +	} +	else if (!strncmp(argv[0], "edit", 4)) { +		if (argc > 1 && strcmp(argv[1], "help") == 0) { +			ipmi_fru_edit_help(); +			return 0; +		} else if (argc < 2) { +			lprintf(LOG_ERR, "Not enough parameters given."); +			ipmi_fru_edit_help(); +			return (-1); +		} +		 +		if (argc >= 2) { +			if (is_fru_id(argv[1], &fru_id) != 0) +				return (-1); + +			if (verbose) { +				printf("FRU ID           : %d\n", fru_id); +			} +		} else { +			printf("Using default FRU ID: %d\n", fru_id); +		} + +		if (argc >= 3) { +			if (!strncmp(argv[2], "field", 5)) { +				if (argc != 6) { +					lprintf(LOG_ERR, "Not enough parameters given."); +					ipmi_fru_edit_help(); +					return (-1); +				} +				rc = ipmi_fru_set_field_string(intf, fru_id, *argv[3], *argv[4], +						(char *) argv[5]); +			} else if (!strncmp(argv[2], "oem", 3)) { +				rc = ipmi_fru_edit_multirec(intf, fru_id, argc, argv); +			} else { +				lprintf(LOG_ERR, "Invalid command: %s", argv[2]); +				ipmi_fru_edit_help(); +				return (-1); +			} +		} else { +			rc = ipmi_fru_edit_multirec(intf, fru_id, argc, argv); +		} +	} +	else if (!strncmp(argv[0], "get", 4)) { +		if (argc > 1 && (strncmp(argv[1], "help", 4) == 0)) { +			ipmi_fru_get_help(); +			return 0; +		} else if (argc < 2) { +			lprintf(LOG_ERR, "Not enough parameters given."); +			ipmi_fru_get_help(); +			return (-1); +		} + +		if (argc >= 2) { +			if (is_fru_id(argv[1], &fru_id) != 0) +				return (-1); + +			if (verbose) { +				printf("FRU ID           : %d\n", fru_id); +			} +		} else { +			printf("Using default FRU ID: %d\n", fru_id); +		} + +		if (argc >= 3) { +			if (!strncmp(argv[2], "oem", 3)) { +				rc = ipmi_fru_get_multirec(intf, fru_id, argc, argv); +			} else { +				lprintf(LOG_ERR, "Invalid command: %s", argv[2]); +				ipmi_fru_get_help(); +				return (-1); +			} +		} else { +			rc = ipmi_fru_get_multirec(intf, fru_id, argc, argv); +		} +	} +	else { +		lprintf(LOG_ERR, "Invalid FRU command: %s", argv[0]); +		ipmi_fru_help(); +		return (-1); +	} + +	return rc; +} + +/* ipmi_fru_set_field_string -  Set a field string to a new value, Need to be the same size.  If +*                              size if not equal, the function ipmi_fru_set_field_string_rebuild +*                              will be called. +* +* @intf:       ipmi interface +* @id:         fru id +* @f_type:    Type of the Field : c=Chassis b=Board p=Product +* @f_index:   findex of the field, zero indexed. +* @f_string:  NULL terminated string +* +* returns -1 on error +* returns 1 if successful +*/ +static int +ipmi_fru_set_field_string(struct ipmi_intf * intf, uint8_t fruId, uint8_t +f_type, uint8_t f_index, char *f_string) +{ +	struct ipmi_rs *rsp; +	struct ipmi_rq req; + +	struct fru_info fru; +	struct fru_header header; +	uint8_t msg_data[4]; +	uint8_t checksum; +	int i = 0; +	int rc = 1; +	uint8_t *fru_data = NULL; +	uint8_t *fru_area = NULL; +	uint32_t fru_field_offset, fru_field_offset_tmp; +	uint32_t fru_section_len, header_offset; + +	memset(msg_data, 0, 4); +	msg_data[0] = fruId; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_INFO; +	req.msg.data = msg_data; +	req.msg.data_len = 1; + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		printf(" Device not present (No Response)\n"); +		rc = (-1); +		goto ipmi_fru_set_field_string_out; +	} +	if (rsp->ccode > 0) { +		printf(" Device not present (%s)\n", +			val2str(rsp->ccode, completion_code_vals)); +		rc = (-1); +		goto ipmi_fru_set_field_string_out; +	} + +	memset(&fru, 0, sizeof(fru)); +	fru.size = (rsp->data[1] << 8) | rsp->data[0]; +	fru.access = rsp->data[2] & 0x1; + +	if (fru.size < 1) { +		printf(" Invalid FRU size %d", fru.size); +		rc = (-1); +		goto ipmi_fru_set_field_string_out; +	} +	/* +	* retrieve the FRU header +	*/ +	msg_data[0] = fruId; +	msg_data[1] = 0; +	msg_data[2] = 0; +	msg_data[3] = 8; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn = IPMI_NETFN_STORAGE; +	req.msg.cmd = GET_FRU_DATA; +	req.msg.data = msg_data; +	req.msg.data_len = 4; + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) +	{ +		printf(" Device not present (No Response)\n"); +		rc = (-1); +		goto ipmi_fru_set_field_string_out; +	} +	if (rsp->ccode > 0) +	{ +		printf(" Device not present (%s)\n", +				val2str(rsp->ccode, completion_code_vals)); +		rc = (-1); +		goto ipmi_fru_set_field_string_out; +	} + +	if (verbose > 1) +		printbuf(rsp->data, rsp->data_len, "FRU DATA"); + +	memcpy(&header, rsp->data + 1, 8); + +	if (header.version != 1) +	{ +		printf(" Unknown FRU header version 0x%02x", +			header.version); +		rc = (-1); +		goto ipmi_fru_set_field_string_out; +	} + +	fru_data = malloc( fru.size ); + +	if( fru_data == NULL ) +	{ +		printf("Out of memory!\n"); +		rc = (-1); +		goto ipmi_fru_set_field_string_out; +	} + +	/* Setup offset from the field type */ + +	/* Chassis type field */ +	if (f_type == 'c' ) { +		header_offset = (header.offset.chassis * 8); +		read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data); +		fru_field_offset = 3; +		fru_section_len = *(fru_data + 1) * 8; +	} +	/* Board type field */ +	else if (f_type == 'b' ) { +		header_offset = (header.offset.board * 8); +		read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data); +		fru_field_offset = 6; +		fru_section_len = *(fru_data + 1) * 8; +	} +	/* Product type field */ +	else if (f_type == 'p' ) { +		header_offset = (header.offset.product * 8); +		read_fru_area(intf ,&fru, fruId, header_offset , 3 , fru_data); +		fru_field_offset = 3; +		fru_section_len = *(fru_data + 1) * 8; +	} +	else +	{ +		printf("Wrong field type."); +		rc = (-1); +		goto ipmi_fru_set_field_string_out; +	} +	memset(fru_data, 0, fru.size); +	if( read_fru_area(intf ,&fru, fruId, header_offset , +					fru_section_len , fru_data) < 0 ) +	{ +		rc = (-1); +		goto ipmi_fru_set_field_string_out; +	} +	/* Convert index from character to decimal */ +	f_index= f_index - 0x30; + +	/*Seek to field index */ +	for (i=0; i <= f_index; i++) { +		fru_field_offset_tmp = fru_field_offset; +		if (fru_area != NULL) { +			free(fru_area); +			fru_area = NULL; +		} +		fru_area = (uint8_t *) get_fru_area_str(fru_data, &fru_field_offset); +	} + +	if( (fru_area == NULL )  || strlen((const char *)fru_area) == 0 ) { +		printf("Field not found !\n"); +		rc = (-1); +		goto ipmi_fru_set_field_string_out; +	} + +	if ( strlen((const char *)fru_area) == strlen((const char *)f_string) ) +	{ +		printf("Updating Field '%s' with '%s' ...\n", fru_area, f_string ); +		memcpy(fru_data + fru_field_offset_tmp + 1, +								f_string, strlen(f_string)); + +		checksum = 0; +		/* Calculate Header Checksum */ +		for( i = header_offset; i < header_offset +						+ fru_section_len - 1; i ++ ) +		{ +			checksum += fru_data[i]; +		} +		checksum = (~checksum) + 1; +		fru_data[header_offset + fru_section_len - 1] = checksum; + +		/* Write the updated section to the FRU data; source offset => 0 */ +		if( write_fru_area(intf, &fru, fruId, 0, +				header_offset, fru_section_len, fru_data) < 0 ) +		{ +			printf("Write to FRU data failed.\n"); +			rc = (-1); +			goto ipmi_fru_set_field_string_out; +		} +	} +	else { +		printf("String size are not equal, resizing fru to fit new string\n"); +		if( +				ipmi_fru_set_field_string_rebuild(intf,fruId,fru,header,f_type,f_index,f_string) +		) +		{ +			rc = (-1); +			goto ipmi_fru_set_field_string_out; +		} +	} + +	ipmi_fru_set_field_string_out: +	if (fru_data != NULL) { +		free(fru_data); +		fru_data = NULL; +	} +	if (fru_area != NULL) { +		free(fru_area); +		fru_area = NULL; +	} + +	return rc; +} + +/* +	This function can update a string within of the following section when the size is not equal: + +	Chassis +	Product +	Board +*/ +/* ipmi_fru_set_field_string_rebuild -  Set a field string to a new value, When size are not +*                                      the same size. +* +*  This function can update a string within of the following section when the size is not equal: +* +*      - Chassis +*      - Product +*      - Board +* +* @intf:     ipmi interface +* @fruId:    fru id +* @fru:      info about fru +* @header:   contain the header of the FRU +* @f_type:   Type of the Field : c=Chassis b=Board p=Product +* @f_index:  findex of the field, zero indexed. +* @f_string: NULL terminated string +* +* returns -1 on error +* returns 1 if successful +*/ + +#define DBG_RESIZE_FRU +static int +ipmi_fru_set_field_string_rebuild(struct ipmi_intf * intf, uint8_t fruId, +											struct fru_info fru, struct fru_header header, +											uint8_t f_type, uint8_t f_index, char *f_string) +{ +	uint8_t msg_data[4]; +	uint8_t checksum; +	int i = 0; +	uint8_t *fru_data_old = NULL; +	uint8_t *fru_data_new = NULL; +	uint8_t *fru_area = NULL; +	uint32_t fru_field_offset, fru_field_offset_tmp; +	uint32_t fru_section_len, old_section_len, header_offset; +	uint32_t chassis_offset, board_offset, product_offset; +	uint32_t chassis_len, board_len, product_len, product_len_new; +	int      num_byte_change = 0, padding_len = 0; +	uint32_t counter; +	unsigned char cksum; +	int rc = 1; + +	fru_data_old = calloc( fru.size, sizeof(uint8_t) ); + +	fru_data_new = malloc( fru.size ); + +	if( fru_data_old == NULL || fru_data_new == NULL ) +	{ +		printf("Out of memory!\n"); +		rc = (-1); +		goto ipmi_fru_set_field_string_rebuild_out; +	} + +	/************************* +	1) Read ALL FRU */ +	printf("Read All FRU area\n"); +	printf("Fru Size       : %u bytes\n", fru.size); + +	/* Read current fru data */ +	read_fru_area(intf ,&fru, fruId, 0, fru.size , fru_data_old); + +	#ifdef DBG_RESIZE_FRU +	printf("Copy to new FRU\n"); +	#endif + +	/************************* +	2) Copy all FRU to new FRU */ +	memcpy(fru_data_new, fru_data_old, fru.size); + +	/* Build location of all modifiable components */ +	chassis_offset = (header.offset.chassis * 8); +	board_offset   = (header.offset.board   * 8); +	product_offset = (header.offset.product * 8); + +	/* Retrieve length of all modifiable components */ +	chassis_len    =  *(fru_data_old + chassis_offset + 1) * 8; +	board_len      =  *(fru_data_old + board_offset   + 1) * 8; +	product_len    =  *(fru_data_old + product_offset + 1) * 8; +	product_len_new = product_len; + +	/* Chassis type field */ +	if (f_type == 'c' ) +	{ +		header_offset    = chassis_offset; +		fru_field_offset = chassis_offset + 3; +		fru_section_len  = chassis_len; +	} +	/* Board type field */ +	else if (f_type == 'b' ) +	{ +		header_offset    = board_offset; +		fru_field_offset = board_offset + 6; +		fru_section_len  = board_len; +	} +	/* Product type field */ +	else if (f_type == 'p' ) +	{ +		header_offset    = product_offset; +		fru_field_offset = product_offset + 3; +		fru_section_len  = product_len; +	} +	else +	{ +		printf("Wrong field type."); +		rc = (-1); +		goto ipmi_fru_set_field_string_rebuild_out; +	} + +	/* Keep length for future old section display */ +	old_section_len = fru_section_len; + +	/************************* +	3) Seek to field index */ +	for (i = 0;i <= f_index; i++) { +		fru_field_offset_tmp = fru_field_offset; +		if (fru_area != NULL) { +			free(fru_area); +			fru_area = NULL; +		} +		fru_area = (uint8_t *) get_fru_area_str(fru_data_old, &fru_field_offset); +	} + +	if( (fru_area == NULL )  || strlen((const char *)fru_area) == 0 ) { +		printf("Field not found (1)!\n"); +		rc = (-1); +		goto ipmi_fru_set_field_string_rebuild_out; +	} + +	#ifdef DBG_RESIZE_FRU +	printf("Section Length: %u\n", fru_section_len); +	#endif + +	/************************* +	4) Check number of padding bytes and bytes changed */ +	for(counter = 2; counter < fru_section_len; counter ++) +	{ +		if(*(fru_data_old + (header_offset + fru_section_len - counter)) == 0) +			padding_len ++; +		else +			break; +	} +	num_byte_change = strlen(f_string) - strlen(fru_area); + +	#ifdef DBG_RESIZE_FRU +	printf("Padding Length: %u\n", padding_len); +	printf("NumByte Change: %i\n", num_byte_change); +	printf("Start SecChnge: %x\n", *(fru_data_old + fru_field_offset_tmp)); +	printf("End SecChnge  : %x\n", *(fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1)); + +	printf("Start Section : %x\n", *(fru_data_old + header_offset)); +	printf("End Sec wo Pad: %x\n", *(fru_data_old + header_offset + fru_section_len - 2 - padding_len)); +	printf("End Section   : %x\n", *(fru_data_old + header_offset + fru_section_len - 1)); +	#endif + +	/* Calculate New Padding Length */ +	padding_len -= num_byte_change; + +	#ifdef DBG_RESIZE_FRU +	printf("New Padding Length: %i\n", padding_len); +	#endif + +	/************************* +	5) Check if section must be resize.  This occur when padding length is not between 0 and 7 */ +	if( (padding_len < 0) || (padding_len >= 8)) +	{ +		uint32_t remaining_offset = ((header.offset.product * 8) + product_len); +		int change_size_by_8; + +		if(padding_len >= 8) +		{ +			/* Section must be set smaller */ +			change_size_by_8 = ((padding_len) / 8) * (-1); +		} +		else +		{ +			/* Section must be set bigger */ +			change_size_by_8 = 1 + (((padding_len+1) / 8) * (-1)); +		} + +		/* Recalculate padding and section length base on the section changes */ +		fru_section_len += (change_size_by_8 * 8); +		padding_len     += (change_size_by_8 * 8); + +		#ifdef DBG_RESIZE_FRU +		printf("change_size_by_8: %i\n", change_size_by_8); +		printf("New Padding Length: %i\n", padding_len); +		printf("change_size_by_8: %i\n", change_size_by_8); +		printf("header.offset.board: %i\n", header.offset.board); +		#endif + +		/* Must move sections */ +		/* Section that can be modified are as follow +			Chassis +			Board +			product */ + +		/* Chassis type field */ +		if (f_type == 'c' ) +		{ +			printf("Moving Section Chassis, from %i to %i\n", +						((header.offset.board) * 8), +						((header.offset.board + change_size_by_8) * 8) +					); +			memcpy( +						(fru_data_new + ((header.offset.board + change_size_by_8) * 8)), +						(fru_data_old + (header.offset.board) * 8), +						board_len +					); +			header.offset.board   += change_size_by_8; +		} +		/* Board type field */ +		if ((f_type == 'c' ) || (f_type == 'b' )) +		{ +			printf("Moving Section Product, from %i to %i\n", +						((header.offset.product) * 8), +						((header.offset.product + change_size_by_8) * 8) +					); +			memcpy( +						(fru_data_new + ((header.offset.product + change_size_by_8) * 8)), +						(fru_data_old + (header.offset.product) * 8), +						product_len +					); +			header.offset.product += change_size_by_8; +		} + +		/* Adjust length of the section */ +		if (f_type == 'c') +		{ +			*(fru_data_new + chassis_offset + 1) += change_size_by_8; +		} +		else if( f_type == 'b') +		{ +			*(fru_data_new + board_offset + 1)   += change_size_by_8; +		} +		else if( f_type == 'p') +		{ +			*(fru_data_new + product_offset + 1) += change_size_by_8; +			product_len_new = *(fru_data_new + product_offset + 1) * 8; +		} + +		/* Rebuild Header checksum */ +		{ +			unsigned char * pfru_header = (unsigned char *) &header; +			header.checksum = 0; +			for(counter = 0; counter < (sizeof(struct fru_header) -1); counter ++) +			{ +				header.checksum += pfru_header[counter]; +			} +			header.checksum = (0 - header.checksum); +			memcpy(fru_data_new, pfru_header, sizeof(struct fru_header)); +		} + +		/* Move remaining sections in 1 copy */ +		printf("Moving Remaining Bytes (Multi-Rec , etc..), from %i to %i\n", +					remaining_offset, +					((header.offset.product) * 8) + product_len_new +				); +		if(((header.offset.product * 8) + product_len_new - remaining_offset) < 0) +		{ +			memcpy( +						fru_data_new + (header.offset.product * 8) + product_len_new, +						fru_data_old + remaining_offset, +						fru.size - remaining_offset +					); +		} +		else +		{ +			memcpy( +						fru_data_new + (header.offset.product * 8) + product_len_new, +						fru_data_old + remaining_offset, +						fru.size - ((header.offset.product * 8) + product_len_new) +					); +		} +	} + +	/* Update only if it's fits padding length as defined in the spec, otherwise, it's an internal +	error */ +	/************************* +	6) Update Field and sections */ +	if( (padding_len >=0) && (padding_len < 8)) +	{ +		/* Do not requires any change in other section */ + +		/* Change field length */ +		printf( +			"Updating Field : '%s' with '%s' ... (Length from '%d' to '%d')\n", +			fru_area, f_string,  +			(int)*(fru_data_old + fru_field_offset_tmp),  +			(int)(0xc0 + strlen(f_string))); +		*(fru_data_new + fru_field_offset_tmp) = (0xc0 + strlen(f_string)); +		memcpy(fru_data_new + fru_field_offset_tmp + 1, f_string, strlen(f_string)); + +		/* Copy remaing bytes in section */ +#ifdef DBG_RESIZE_FRU +		printf("Copying remaining of sections: %d \n", +		 (int)((fru_data_old + header_offset + fru_section_len - 1) - +		 (fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1))); +#endif + +		memcpy((fru_data_new + fru_field_offset_tmp + 1 +  +			strlen(f_string)), +			(fru_data_old + fru_field_offset_tmp + 1 +  +			strlen(fru_area)), +		((fru_data_old + header_offset + fru_section_len - 1) - +		(fru_data_old + fru_field_offset_tmp + strlen(f_string) + 1))); + +		/* Add Padding if required */ +		for(counter = 0; counter < padding_len; counter ++) +		{ +			*(fru_data_new + header_offset + fru_section_len - 1 -  +			  padding_len + counter) = 0; +		} + +		/* Calculate New Checksum */ +		cksum = 0; +		for( counter = 0; counter <fru_section_len-1; counter ++ ) +		{ +			cksum += *(fru_data_new + header_offset + counter); +		} +		*(fru_data_new + header_offset + fru_section_len - 1) = (0 - cksum); + +		#ifdef DBG_RESIZE_FRU +		printf("Calculate New Checksum: %x\n", (0 - cksum)); +		#endif + +		/****** ENABLE to show section modified before and after ********/ +		#if 0 +		printf("Section: "); +		for( counter = 0; counter <old_section_len; counter ++ ) +		{ +			if((counter %16) == 0) +			{ +				printf("\n"); +			} +			printf( "%02X ", *(fru_data_old + header_offset + counter) ); +		} +		printf("\n"); + +		printf("Section: "); +		for( counter = 0; counter <fru_section_len; counter ++ ) +		{ +			if((counter %16) == 0) +			{ +				printf("\n"); +			} +			printf( "%02X ", *(fru_data_new + header_offset + counter) ); +		} +		printf("\n"); +		#endif +	} +	else +	{ +		printf( "Internal error, padding length %i (must be from 0 to 7) ", padding_len ); +		rc = (-1); +		goto ipmi_fru_set_field_string_rebuild_out; +	} + +	/************************* +	7) Finally, write new FRU */ +	printf("Writing new FRU.\n"); +	if( write_fru_area( intf, &fru, fruId, 0, 0, fru.size, fru_data_new ) < 0 ) +	{ +		printf("Write to FRU data failed.\n"); +		rc = (-1); +		goto ipmi_fru_set_field_string_rebuild_out; +	} + +	printf("Done.\n"); + +	ipmi_fru_set_field_string_rebuild_out: +	if (fru_area != NULL) { +		free(fru_area); +		fru_area = NULL; +	} +	if (fru_data_new != NULL) { +		free(fru_data_new); +		fru_data_new = NULL; +	} +	if (fru_data_old != NULL) { +		free(fru_data_old); +		fru_data_old = NULL; +	} + +	return rc; +} | 
