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; +} |